STL string 源码剖析2 string对象模型

string对象模型
上篇剖析的string对象模型
template <class _Tp, class _Alloc> class _String_base {
....
  _Tp* _M_start;
  _Tp* _M_finish;
  _Tp* _M_end_of_storage;
};
template <class _CharT, class _Traits, class _Alloc> 
class basic_string : private _String_base<_CharT,_Alloc> {
.....
static const size_type nops;
};

  从上面的代码可以看到string 内存布局就是三个指针,在64位下就是24字节,但是这个版本的stl 比较老,它是stl源码剖析一书中对应的版本特别的老。本文接下来剖析下gcc 4.4.6 版本string对象模型 ,这个版本的string 属于写时拷贝的版本。

gcc 4.4.6 版本string 构造函数源码分析
struct _Alloc_hider : _Alloc
{
  _Alloc_hider(_CharT* __dat, const _Alloc& __a)
  : _Alloc(__a), _M_p(__dat) { }
  _CharT* _M_p; 
 };
template<typename _CharT, typename _Traits, typename _Alloc>
class basic_string
{
  ...
  mutable _Alloc_hider	_M_dataplus;
  ...
}

  可以看到在 gcc 4.4.6 中string 在栈上就只有一个 char * 的指针。那么它是如何跟它引用计数关联起来的,可以看下面的代码,在构造的string 对象的时候,里面调用了Ref的create 函数来构造string对象,然后通过 M_ref_data 函数返回真正的字符串地址给string对象的 _M_dataplus指针。
  其实总结下string真正在堆上的对象是通过 ref::create 函数来申请的,这个函数会申请 struct ref + len+1 个空间,然后使用 置位new 来首先在这个空间的最上面初始化 Ref 结构体对象,然后通过调用 M_ref_data 函数 对象申请的空间 p 进行加1 ,然后指针达到 char * 的位置,然后把这个位置的指针返回回去,外部的 _M_dataplus 指针接受,所以栈上的 _M_dataplus 指向的并不是string在堆上的全部空间只是一部分。

  template<typename _CharT, typename _Traits, typename _Alloc>
  inline basic_string<_CharT, _Traits, _Alloc>::basic_string()
    : _M_dataplus(_S_construct(size_type(), _CharT(), _Alloc()), _Alloc()) {}

template<typename _CharT, typename _Traits, typename _Alloc>
template<typename _InIterator>
 _CharT* basic_string<_CharT, _Traits, _Alloc>::_S_construct(_InIterator __beg, _InIterator __end, const _Alloc& __a,
		   input_iterator_tag)
  {
    ...
	_CharT __buf[128];
	size_type __len = 0;
	while (__beg != __end && __len < sizeof(__buf) / sizeof(_CharT))
	  {
	    __buf[__len++] = *__beg;
	    ++__beg;
	  }
	_Rep* __r = _Rep::_S_create(__len, size_type(0), __a);//这个里调用了create 函数 
	_M_copy(__r->_M_refdata(), __buf, __len);
	__try
	  {
	    while (__beg != __end)
	      {
		if (__len == __r->_M_capacity)
		  {
		    // Allocate more space.
		    _Rep* __another = _Rep::_S_create(__len + 1, __len, __a);
		    _M_copy(__another->_M_refdata(), __r->_M_refdata(), __len);
		    __r->_M_destroy(__a);
		    __r = __another;
		  }
		__r->_M_refdata()[__len++] = *__beg;
		++__beg;
	      }
	  }
   ....
	__r->_M_set_length_and_sharable(__len);
	return __r->_M_refdata();
}
template<typename _CharT, typename _Traits , typename _Alloc>
typename basic_string <_CharT, _Traits, _Alloc >::_Rep*
basic_string<_CharT , _Traits, _Alloc>::_Rep ::
_S_create(size_type __capacity, size_type __old_capacity ,
          const _Alloc & __alloc)
{
    // 需要分配的空间包括:
    //  一个数组 char_type[__capacity]
    //  一个额外的结尾符 char_type()
    //  一个足以容纳 struct _Rep 空间
    // Whew. Seemingly so needy, yet so elemental.
    size_type __size = (__capacity + 1) * sizeof( _CharT) + sizeof (_Rep);
    void* __place = _Raw_bytes_alloc (__alloc). allocate(__size ); //申请空间
    _Rep * __p = new (__place) _Rep;// 在地址__place 空间上直接 new对象( 称为placement new)
    __p-> _M_capacity = __capacity ;
    __p-> _M_set_sharable();// 设置引用计数为0,标明该对象只为自己所有
    return __p;
}
operator [] 分析

operator [] 函数进行俩步操作

  1. 查看是否引用计数大于1,大于1进入第二步逻辑,否则直接进入第三步逻辑
  2. 拷贝一份新的Rep结构,然后把原有的引用计数减一,设置好当前对象的新引用计数
  3. 返回对应char的引用
对象模型图
gcc 4.4.6

在这里插入图片描述

本博客上篇剖析的 string 对象模型

默认存一个 字符串 a 的对象模型
在这里插入图片描述

gcc 4.4.6 写时拷贝的string 可能引起的bug

  如果我们直接通过 string::data / c_str 的返回值去修改string 内容的时候会修改所有拷贝过这个对象的其他string对象。

string origin("A");
string copy(origin);
char * p = const_cast<char*>(origin.data());
p[0] = 'B';
cout << origin; //B
cout << copy;//B
参考

https://blog.csdn.net/sdoyuxuan/article/details/76230520
http://blogs.360.cn/post/linux-gcc-stl-string-in-depth.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值