(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的源码,就会比较清楚了。
先提一个概念转换:
末尾添加结束符 转化为 末尾添加一个类型的缺省构造
使用类型的缺省构造 char() wchar_t() char16_t() char32_t() 的值都为0,类型的缺省构造也就字符串的结束符了。
所以末尾添加一个类型的缺省构造,对字符而言,就是添加了结束符啊。
这种方式后续自己写代码的时候,也可以利用;
概念转变了,思路也就打开了。
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;
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;
_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
_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 }
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 }
赋值时就要考虑资源长度问题了:
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)