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

std::string的拷贝赋值研究的更多相关文章

  1. std::string的find问题研究

    https://files-cdn.cnblogs.com/files/aquester/std之string的find问题研究.pdf 目录 目录 1 1. 前言 1 2. find字符串 1 3. ...

  2. QString, Std::string, char *相互转换

    Qt 库中对字符串类型进行了封装,QString 类提供了所有字符串操作方法,给开发带来了便利. 由于第三方库的类型基本上都是标准的类型,即使用std::string或char *来表示字符 (串) ...

  3. 标准C++类std::string的内存共享和Copy-On-Write(写时拷贝)

    标准C++类std::string的内存共享,值得体会: 详见大牛:https://www.douban.com/group/topic/19621165/ 顾名思义,内存共享,就是两个乃至更多的对象 ...

  4. 使用 istreambuf_iterator 读取文件内容,赋值给 std::string

    需要一个一个字符输入时考虑使用istreambuf_iterator 假设我们要把一个文本文件拷贝到一个字符串对象中.似乎可以用一种很有道理的方法完成: ifstream inputFile(&quo ...

  5. C++的std::string的“读时也拷贝”技术!

    C++的std::string的读时也拷贝技术! 嘿嘿,你没有看错,我也没有写错,是读时也拷贝技术.什么?我的错,你之前听说写过时才拷贝,嗯,不错的确有这门技术,英文是Copy On Write,简写 ...

  6. std::string 赋值为nullptr引起程序崩溃

    一个错误排查两天,std::string赋初值时最好为"", 如果赋初值为nullptr,因为std::string不能和nullptr作比较,所以后面用的时候会引起崩溃. 佩服我 ...

  7. 【转】标准C++类std::string的内存共享和Copy-On-Write技术

    1.             概念 Scott Meyers在<More Effective C++>中举了个例子,不知你是否还记得?在你还在上学的时候,你的父母要你不要看电视,而去复习功 ...

  8. 源码阅读笔记 - 3 std::string 与 Short String Optimization

    众所周知,大部分情况下,操作一个自动(栈)变量的速度是比操作一个堆上的值的速度快的.然而,栈数组的大小是在编译时确定的(不要说 C99 的VLA,那货的 sizeof 是运行时计算的),但是堆数组的大 ...

  9. c++ std::string 用法

    std::string用法总结 在平常工作中经常用到了string类,本人记忆了不好用到了的时候经常要去查询.在网上摘抄一下总结一下,为以后的查询方便: string类的构造函数: string(co ...

随机推荐

  1. dede织梦后台页面及功能修改精简操作方法

    有很多使用织梦程序的站长往往都不喜欢使用默认的后台,但对于很多小白站长其实也不太懂程序的功能,而且如果显示或者开了过多的功能只会给自己带来困扰,所以小白站长都喜欢一些傻瓜式的后台操作界面.那么,ded ...

  2. sqlserver to oracle

    SELECT c.*, d .Organization_Name, d .ParentId, e.Roles_ID, e.Roles_Name FROM ( SELECT a.*, b.Organiz ...

  3. Java 日期比较大小

    import org.junit.Test; import java.text.SimpleDateFormat; import java.util.Date; /** * @author DateJ ...

  4. 【函数】raise 函数(小窗help)

    在Python中,要想引发异常,最简单的形式就是输入关键字raise,后跟要引发的异常的名称. 异常名称标识出具体的类: Python异常处理是那些类的对象. 执行raise语句时,Python会创建 ...

  5. 如何解决python 图表中文显示乱码问题(matlplotlib 包)

    目前搜到的是,下载一个字体到程序路径,设置成默认字体.  https://blog.csdn.net/irene_loong/article/details/68955485 #图表显示中文设置 im ...

  6. CentOS开机自动运行自己的脚本详解

    一.root权限编辑/etc/rc.d/rc.local su cd /etc/rc.d/ vi rc.local 二.在这个文件加上你要执行的脚本,全部内容如下: #!/bin/sh # # Thi ...

  7. 记账本,C,Github,entity

    package entity; public class Category { private int id; private String name; private int recordNumbe ...

  8. Object.assign()怎么用?

    用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target). 1.以对象为参数的合并1)Object.assign方法的第一个参数是目标对象,后面的参数都是源对象 con ...

  9. 《Effective C++》笔记

    01:视c++为一个语言联邦 为了理解C++,必须要认识其主要的次语言: C 说到底C++仍是以C为基础.区块,语句,预处理器,内置数据类型,数组,指针统统来自C. Object-Oreinted C ...

  10. 微信小程序记账本进度六

    //app.jsApp({ onLaunch: function () { //调用API从本地缓存中获取数据 var logs = wx.getStorageSync('logs') || [] l ...