自从GCC-5.1开始,std::string引入了遵从C++11标准的新实现,默认使用SSO(small string optimization)特性,禁用了写时复制(COW)引用计数机制,这也带来了与旧版本std::string的ABI兼容性问题,本篇结合GCC-5.3.0源码来分析新的std::string实现。
先来对比下GCC-5.3.0与GCC-4.8.5下basic_string类声明的区别:
// GCC-5.3.0 template<typename _CharT, typename _Traits, typename _Alloc> class basic_string { private: // Use empty-base optimization: http://www.cantrip.org/emptyopt.html struct _Alloc_hider : allocator_type // TODO check __is_final { _Alloc_hider(pointer __dat, const _Alloc& __a = _Alloc()) : allocator_type(__a), _M_p(__dat) { } pointer _M_p; // The actual data. }; _Alloc_hider _M_dataplus; size_type _M_string_length; enum { _S_local_capacity = 15 / sizeof(_CharT) }; union { _CharT _M_local_buf[_S_local_capacity + 1]; size_type _M_allocated_capacity; }; }; // GCC-4.8.5 template<typename _CharT, typename _Traits = char_traits<_CharT>, typename _Alloc = allocator<_CharT> > class basic_string; typedef basic_string<char> string; template<typename _CharT, typename _Traits, typename _Alloc> class basic_string { private: struct _Rep_base { size_type _M_length; size_type _M_capacity; _Atomic_word _M_refcount; }; struct _Rep : _Rep_base { }; // Use empty-base optimization: http://www.cantrip.org/emptyopt.html struct _Alloc_hider : _Alloc { _Alloc_hider(_CharT* __dat, const _Alloc& __a) : _Alloc(__a), _M_p(__dat) { } _CharT* _M_p; // The actual data. }; mutable _Alloc_hider _M_dataplus; _CharT* _M_data() const { return _M_dataplus._M_p; } _Rep* _M_rep() const { return &((reinterpret_cast<_Rep*> (_M_data()))[-1]); } };
GCC-5.3.0下std::string中使用了三个私有成员:实际持有的数据指针_M_dataplus、数据实际大小_M_string_length、针对短小字符串优化的默认16字节大小的匿名union对应的栈上内存,因此sizeof(std::string) == 32,而且已不再采用引用计数机制,每个对象真正持有实际数据,线程安全。
而GCC-4.8.5下std::string采用了写时复制的引用计数(_Rep类),内部仅有一个实际持有的数据指针_M_dataplus,因此sizeof(std::string) == 8,而且由COW实现也可见多线程下使用不安全。
对于新的std::string实现导致的不同版本GCC编译导致的二进制不兼容问题,GCC5 提供了相应的二进制兼容宏开关_GLIBCXX_USE_CXX11_ABI。