说明:以下涉及的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. 爬虫:输入网页之后爬取当前页面的图片和背景图片,最后打包成exe

    环境:py3.6 核心库:selenium(考虑到通用性,js加载的网页).pyinstaller 颜色显示:colors.py colors.py  用于在命令行输出文字时,带有颜色,可有可无. # ...

  2. JavaWeb——jsp-config

    <jsp-config> 包括 <taglib> 和 <jsp-property-group> 两个子元素.其中<taglib> 元素在JSP 1.2 ...

  3. XSY contest1586 proB

    题目 现在一圈n个花坛, 每次随机往一个花盆里种花, 一个花盆可以种多颗花, 假如一个花盆两边的花盆都有花, 那么他也将被种上花 问期望种满所有花盆要种几次 首先定义f(i)为放置了i个物品后完全覆盖 ...

  4. Oracle SQL 优化规则

    一.问题的提出 在应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句,复杂视图的的编写等体会不出SQL语句各种写法的性能优劣,但是如果将应用 系统提交实际应用后,随着数据库中数据的增加,系 ...

  5. leetcode每日刷题计划-简单篇day2

    今天数模比赛爆肝&操作系统大作业 脖子疼orz先把题过了保证flag不倒..个别细节回头看吧 Num 13 罗马数字转整数 Roman to Integer 一遍提交过,开始编译出了点问题 具 ...

  6. py库: flask笔记

    http://flask.pocoo.org/ http://flask.pocoo.org/docs/0.12/api/#api API http://docs.pythontab.com/flas ...

  7. python tp_ready函数分析

    int PyType_Ready(PyTypeObject *type) { PyObject *dict, *bases; PyTypeObject *base; Py_ssize_t i, n; ...

  8. leetcode4

    public class Solution { public double FindMedianSortedArrays(int[] nums1, int[] nums2) { var nums = ...

  9. leetcode1032

    class StreamChecker: def __init__(self, words: 'List[str]'): self.maxLen = 0 self.List = set(words) ...

  10. Linux输入子系统详解

    input输入子系统框架  linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Input ...