(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
参考:std::string源码
参考:https://www.cplusplus.com/reference/string/string/
std::string确实是一个很棒的类结构。之前使用时,还在想,std::string如何保存末尾的字符串结束符,std::wstring时呢又是如何;一般在vs上调试查看时并没有看到字符串结束符啊;另外结束符算不算到size里面去呢,要算到分配的空间里面去吧;带着这些疑问,去看下string的源码,就会比较清楚了。
1. 概念:字符末尾结束符==末尾类型缺省构造
先提一个概念转换:
末尾添加结束符 转化为 末尾添加一个类型的缺省构造
使用类型的缺省构造 char() wchar_t() char16_t() char32_t() 的值都为0,类型的缺省构造也就字符串的结束符了。
所以末尾添加一个类型的缺省构造,对字符而言,就是添加了结束符啊。
这种方式后续自己写代码的时候,也可以利用;
概念转变了,思路也就打开了。
2. basic_string存储
string来源于basic_string,算是这个模版类的一个实例,主要的功能实现也都在basic_string中描述的。
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
typedef basic_string<char16_t> u16string;
typedef basic_string<char32_t> u32string;
2.1 存储的成员变量
std::string存储的变量:_Mypair;_Compressed_pair该结构封装了两个值的访问,没有特殊的用途。对于string存储而言,重点关注_String_val这个结构。
// using _Alty = typename _Alloc_types::_Alty;
// using _Val_types = typename _Alloc_types::_Val_types;
using _Mydata_t = _String_val<_Val_types>;
_Compressed_pair<_Alty, _Mydata_t> _Mypair;
2.2 _String_val结构定义
_String_val的结构定义:这个结构是了解string结构的关键,它的成员变量有三个,每个都很关键:
union _Bxty _Bx; // store short buffer or store pointer
size_type _Mysize; // current length of string
size_type _Myres; // current storage reserved for string
- _Bx的union结构提供16位长度内存块,存储小于16个长度时,直接使用;
- _Bx提供指针,大于等于16位长度时,分配内存放在该union结构指针里,供存储使用;
- _Mysize记录字符串长度;
- _Myres记录当前已分配资源块长度;
_String_val总体代码:
template<class _Val_types>
class _String_val : public _Container_base
{ // base class for basic_string to hold data
public:
_String_val() : _Bx(), _Mysize(0), _Myres(0) {}
value_type *_Myptr() {
value_type *_result = _Bx._Buf;
if(_Large_string_engaged()) _result = _Unfancy(_Bx._Ptr);
return _result;
}
const value_type *_Myptr() const ....
bool _Large_string_engaged() const { return (_BUF_SIZE <= _Myres); }
void _Check_offset(const size_type off) const { if (_Mysize < _Off) _Xran(); }
void _Check_offset_exclusize(const size_type _Off) const { if (_Mysize <= _Off) _Xran();}
static void _Xran(){ _Xout_of_range("Invalid string position"); }
size_type _Clamp_suffix_size(const size_type _Off, const size_type _Size) const noexcept { return (_Min_value(_Size, _Mysize - _Off)); }
// variables
enum { _BUF_SIZE = 16 / (sizeof(value_type) < 1 ? 1 : 16 / sizeof(value_type)) }
enum { _ALLOC_MASK = sizeof(value_type) <= 1 ? 15 : sizeof (value_type) <= 2 ? 7 : sizeof(value_type) <=4 ? 3 : sizeof(value_type <= 8)? 1: 0 };
union _Bxty{ // storage for small
_Bxty = defaut; ~_Bxty = default();
value_type _Buf[_BUF_SIZE];
pointer _Ptr;
char _Alias[_BUF_SIZE];
} _Bx;
size_type _Mysize; // current length of string
size_type _Myres; // current storage reserved for string
}
3. std::string存储使用
string存储在哪个结构中明确了,再看看使用时,就可以解答我们的一些问题了。
以下面的这个构造为例:
std::string str(8, 'a')
它会调用到basic_string的长度+字符构造函数,在这个构造函数中有两个关键的点,一个初始化字符串的_String_val结构,一个把值设入这个结构中。
// std::string的长度+字符构造方式:
basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) : _Mybase(){
_Tidy_init();
assign(_Count, _Ch);
}
初始化就比较简单了:字符串初始长度为0,资源先考虑用union的资源并初始化。
void _Tidy_init(){
auto &_My_data = this->_Get_data(); // 获取到_String_val
_My_data._Mysize = 0; // 字符长度设0
_My_data._Myres = this->_BUF_SIZE - 1; // 资源长度设15
_Traits::assign(_My_data._Bx._Buf[0], _Elem()); // 资源首字符设\0
}
赋值时就要考虑资源长度问题了:
- 基于资源长度,判定使用内部的union的块来存,还是申请内存来存,申请时会增加一些长度的;
- 数据设置完,最后都会再设置一个类型缺省构造上去,也就是字符结束符。我们已知对与char类型_Elem() = char() = ‘\0’,这样的话最后一位之后多设置一个_Elem()就成就了末尾带’\0’了。
basic_string::assign(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch){ // assign _Count * _ch
auto &_My_data = this->_Get_data();
if (_Count < _My_data._Myres){
_Elem* const _Old_ptr = _My_data._Myptr();
_My_data._Mysize = _Count;
_Traits::assign(_Old_ptr, _Count, _ch);
_Traits::assign(_Old_ptr[_Count], _Elem());
return (*this);
}
return (_Reallocate_for(_Count, [](Elem* const _New_ptr, const size_type _Count, const _Elem _ch){
_Traits::assign(_New_ptr, _Count, _ch);
_Traits::assign(_New_ptr[_Count], _Elem());}, _Ch);
}
}
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)