绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
std::string的拷贝赋值研究
2019-09-11 16:05:01

说明:以下涉及的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;

}

分享好友

分享这个小栈给你的朋友们,一起进步吧。

应用开发
创建时间:2020-06-17 15:31:04
应用软件开发是指使用程序语言C#、java、 c++、vb等语言编写,主要是用于商业、生活应用的软件的开发。
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

技术专家

查看更多
  • 栈栈
    专家
戳我,来吐槽~