std::string存储结构与字符串结束符

(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
  1. _Bx的union结构提供16位长度内存块,存储小于16个长度时,直接使用;
  2. _Bx提供指针,大于等于16位长度时,分配内存放在该union结构指针里,供存储使用;
  3. _Mysize记录字符串长度;
  4. _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
}

赋值时就要考虑资源长度问题了:

  1. 基于资源长度,判定使用内部的union的块来存,还是申请内存来存,申请时会增加一些长度的;
  2. 数据设置完,最后都会再设置一个类型缺省构造上去,也就是字符结束符。我们已知对与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)

上一篇:Android_Basic_01.关于单例对象的使用


下一篇:remote: HTTP Basic:Access denied fatal:Authentication failed for