zoukankan      html  css  js  c++  java
  • std::string的拷贝赋值研究

    说明:以下涉及的std::string的源代码摘自4.8.2版本。
    结论:std::string的拷贝复制是基于引用计数的浅拷贝,因此它们指向相同的数据地址。

    // std::string类定义
    typedef basic_string<char> string;
    template<typename _CharT, typename _Traits, typename _Alloc>
    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<void*>(&_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 <stdio.h>
    #include <string>

    // 如果没有为结构X提供赋值函数,
    // 则编译器生成默认的赋值函数
    struct X {
    std::string str;
    };

    int main() {
    struct X x1, x2;
    x1.str = "abc";
    // X2指向的_S_empty_rep_storage
    printf("%p, %p ", 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 ", x1.str.c_str(), x2.str.c_str());

    // 下面输出的x1和x2数据地址必然不同
    x2.str = "123";
    printf("%p, %p ", x1.str.c_str(), x2.str.c_str());
    return 0;
    }

  • 相关阅读:
    Java 中的按值传递
    字符串排序(非字典排序)
    字符串匹配的KMP算法(转)
    效率更高的整数转化为字符串函数
    Trie 树(转)
    C 语言字符串(译)
    linux 下 epoll 编程
    CSS攻击:记录用户密码
    Wireshark(抓包神器)使用方法
    搭建KVM环境——Linux上安装KVM带web管理界面
  • 原文地址:https://www.cnblogs.com/aquester/p/10531183.html
Copyright © 2011-2022 走看看