探究C++中string类的实现原理以及扩展使用

2020-01-06 14:08:06王旭

从这个定义可以得出,针对char和wchar_t它的值分别是16和8。
_Bxty是一个union:


union _Bxty 
{  // storage for small buffer or pointer to larger one 
  _Elem _Buf[_BUF_SIZE]; 
  _Elem *_Ptr; 
} _Bx; 

为什么要那样定义_Bxty呢,看下面这段代码:


_Elem * _Myptr() 
{  // determine current pointer to buffer for mutable string 
  return (_BUF_SIZE <= _Myres ? _Bx._Ptr : _Bx._Buf); 
}

  
这个函数返回basic_string内部的元素指针(c_str函数就是调用这个函数)。
所以当元素个数小于_BUF_SIZE时不用分配内存,直接使用_Buf数组,_Myptr返回_Buf。否则就要分配内存了,_Myptr返回_Ptr。

不过内存分配策略又是怎样的呢?看下面这段代码:


void _Copy(size_type _Newsize, size_type _Oldlen) 
{  // copy _Oldlen elements to newly allocated buffer 
  size_type _Newres = _Newsize | _ALLOC_MASK; 
  if (max_size() < _Newres) 
    _Newres = _Newsize; // undo roundup if too big 
  else if (_Newres / 3 < _Myres / 2 && _Myres <= max_size() - _Myres / 2) 
    _Newres = _Myres + _Myres / 2; // grow exponentially if possible 
  //other code 
} 

_ALLOC_MASK的值是_BUF_SIZE-1。这段代码看起来有点复杂,简单描述就是:最开始_Myres每次增加_BUF_SIZE,当值达到一定大小时每次增加一半。
针对char和wchar_t,每次分配内存的临界值分别是(超过这些值就要重新分配):


char:15,31,47,70,105,157,235,352,528,792,1188,1782。。。
wchar_t:7, 15, 23, 34, 51, 76, 114, 171, 256, 384, 576, 864, 1296, 1944。。。

重新分配后都会先将旧的元素拷贝到新的内存地址。所以当处理一个长度会不断增长而又大概知道最大大小时可以先调用reserve函数预分配内存以提高效率。

string类占多少字节的内存空间呢?

_Container_base Debug下含有一个指针,4字节,Release下是空类,0字节。_String_val类含有一个allocator对象。string类使用默认的allocator类,这个类没有数据成员,不过按字节对齐的原则,它占4字节。basic_string类的成员加起来是24,所以总共是32字节(Debug)或28字节(Relase)。wstring也是32或28,至于原因文中已经分析。


综上所述:string和wstring类借助_String_iterator实现迭代器操作,都占32(Debug)或28(Release)字节的内存空间,没有虚函数,构造和析构开销较低,内存分配比较灵活。