std::string的拷贝赋值研究

说明:以下涉及的std::string的源代码摘自4.8.2版本。 结论:std::string的拷贝复制是基于引用计数的浅拷贝,因此它们指向相同的数据地址。 // std::string类定义 typedef basic_string string; template class basic_string { private:     // _Alloc_hider是模板类basic_string内嵌struct     struct _Alloc_hider : _Alloc     {         //  唯一构造函数,         // 在构造时使用第一个参数__dat初始化_M_p         _Alloc_hider(_CharT* __dat, const _Alloc& __a)             : _Alloc(__a), _M_p(__dat)        {}         // _M_p为实际存储数据的地方         _CharT* _M_p; // The actual data.     }; private:     _CharT* _M_data() const     { return  _M_dataplus._M_p; }     // 浅拷贝,     // 这正是x2=x1后,两者数据地址相同的原因     _CharT* _M_data(_CharT* __p)     { return (_M_dataplus._M_p = __p); }     _Rep* _M_rep() const     {         // 这里数组下标是“-1”         return &((reinterpret_cast<_Rep*>(_M_data()))[-1]);     }     // 维护引用计数     struct _Rep_base     {         size_type _M_length;         size_type _M_capacity;         _Atomic_word _M_refcount;     };     // _Rep是模板类basic_string内嵌struct     struct _Rep : _Rep_base     {         // The following storage is init'd to 0 by the linker,          // resulting (carefully) in an empty string with one reference.         // 空的std::string实际都指向了_S_empty_rep_storage,         // 因此它们的数据地址是相同的         static size_type _S_empty_rep_storage[];         static _Rep& _S_empty_rep()         {             void* __p = reinterpret_cast(&_S_empty_rep_storage);             return *reinterpret_cast<_Rep*>(__p);         }         _CharT* _M_grab(const _Alloc& __alloc1, const _Alloc& __alloc2)         {           return (!_M_is_leaked() && __alloc1 == __alloc2)                   ? _M_refcopy() : _M_clone(__alloc1);         }         _CharT* _M_refcopy() throw()         {         #if _GLIBCXX_FULLY_DYNAMIC_STRING == 0             if (__builtin_expect(this != &_S_empty_rep(), false))         #endif             __gnu_cxx::__atomic_add_dispatch(&this->_M_refcount, 1);             return _M_refdata();         }  // XXX MT         _CharT* _M_refdata() throw()         { return reinterpret_cast<_CharT*>(this + 1); }     }; public:     static _Rep& _S_empty_rep()     {         return _Rep::_S_empty_rep();     }     // 不带参数的默认构造函数     // 测试环境_GLIBCXX_FULLY_DYNAMIC_STRING值为0,     // 因此只需要关注_S_empty_rep     basic_string() #if _GLIBCXX_FULLY_DYNAMIC_STRING == 0         : _M_dataplus(_S_empty_rep()._M_refdata(), _Alloc())         { } #else         : _M_dataplus(_S_construct(size_type(), _CharT(), _Alloc()), _Alloc())         { } #endif     basic_string& assign(const basic_string& __str)     {         // 如果已经相同,则什么也不用做         if (_M_rep() != __str._M_rep())         {             const allocator_type __a = this->get_allocator();             _CharT* __tmp = __str._M_rep()->_M_grab(__a, __str.get_allocator());             _M_rep()->_M_dispose(__a);             _M_data(__tmp);         }         return *this;     } #if __cplusplus >= 201103L     basic_string& assign(basic_string&& __str)     {         this->swap(__str);         return *this;     } #endif // C++11     basic_string& operator=(const basic_string& __str)     {          return this->assign(__str);      } private:     // mutable表明const成员函数会修改_M_dataplus     mutable _Alloc_hider _M_dataplus; }; // 测试代码 // 编译命令: // g++ -g -o x x.cpp -D_GLIBCXX_DEBUG #include #include // 如果没有为结构X提供赋值函数, // 则编译器生成按位的赋值函数 struct X {     std::string str; }; int main() {     struct X x1, x2;     x1.str = "abc";     // X2指向的_S_empty_rep_storage     printf("%p, %p\n", x1.str.c_str(), x2.str.c_str());     // (gdb) p x1.str._M_dataplus._M_p     // (gdb) p x2.str._M_dataplus._M_p     // 拷贝赋值函数采用的是引用计数,     // 所以x1和x2的数据地址是相同的     x2 = x1;     printf("%p, %p\n", x1.str.c_str(), x2.str.c_str());     // 下面输出的x1和x2数据地址必然不同     x2.str = "123";     printf("%p, %p\n", x1.str.c_str(), x2.str.c_str());     return 0; }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券