测大小
这里我们比较4种版本的basic_string模版类,分别是:string
, wstring
, u16string
, u32string
. 虽然他们是不同的字符串类型,但是其sizeof的结果却一样,都为40字节(x64下):
看源码
我们都知道,上面4个不同的类型是同一份模版(basic_string)的实例化,因此它们的内存模型都是相同的。 这里我们来追踪一下string类型的源代码,源码引用VS2013版的标准string库。
看继承
看得出,basic_string的继承体系非常复杂(当前无关部分我用...
代替了)。
1. typedef basic_string<char, char_traits<char>, allocator<char> >
string;
↓↓↓
2. class basic_string
: public _String_alloc<!is_empty<_Alloc>::value,
_String_base_types<_Elem, _Alloc> > {...}
↓↓↓
3. class _String_alloc
: public _String_val<typename _Alloc_types::_Val_types> {...}
↓↓↓
4. class _String_val
: public _Container_base {...}
↓↓↓
5. typedef _Container_base12 _Container_base;
↓↓↓
6. struct _Container_base12 {...}
看成员
光有继承体系还不够,我们还得看看类成员。 注意,这里不需要关心模版参数,至于为什么,你们自己好好想想喽 >-<)
看定义
能够看到,_Container_base12
类只包含一个指针(指向一个代理类,这里我们不深究),大小永远为1个字长。 因此,我们主要来看看_String_val
类的成员定义:
1. enum
{ // length of internal buffer, [1, 16]
_BUF_SIZE = 16 / sizeof (value_type) < 1 ? 1
: 16 / sizeof (value_type)};
union _Bxty
{ // storage for small buffer or pointer to larger one
value_type _Buf[_BUF_SIZE];
pointer _Ptr;
char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;
2. size_type _Mysize; // current length of string
3. size_type _Myres; // current storage reserved for string
其中,_BUF_SIZE
定义了缓冲区的长度:
- 若value_type类型小于1字节,则 _BUF_SIZE = 16。
- 否则,_BUF_SIZE = 16 / sizeof(value_type)。(留心,这里是整除取商!!!)
析类型
这里有几个类型定义非常重要:value_type
, pointer
, size_type
。 从源代码上看,这些类型经过了一层又一层的typedef,源头难辨。 因此,我们转而从标准草案来先解读value_type
:
typedef typename traits::char_type value_type;
X::char_type -> charT
charT -> char
其它两个类型也依理分析,最后得出它们的实际类型分别为:
得结果
综上,字符串类型内存模型如下:
最后让我们拿string类型来验证一下(><良心保证,下图为代码运行结果,非纯数字打印):