std::string笔记

std::string

简述

C++ 中有着一个标准实现的 Standard Template Library(STL),其中 std::string 是其中一个经常被使用的特化模板类(特化类型为 std::basic_string<char>)。

出于 C++ ISO 标准委员会的效率和耦合性考虑,实际上 std::stringstd::vector<char> 相差不大,底层实现原理基本上与之相同,接口函数也基本上一模一样。不过实际上 std::string 是经过特殊优化的,例如底层实际上对小字符串进行了一定程度的优化,避免频繁请求堆内存空间。

底层实现与内存空间

以 GCC 为例,std::string 的设计可以简化为:

1
2
3
4
5
6
7
8
9
10
11
union __string_value
{
char local[16]; // 小字符串的本地空间
size_t nMaxSize; // 容器已分配的空间大小
};
struct string
{
char* actual; // 容器的堆指针 (只有当字符串大小超过16才有效)
__string_value value; // 容器的缓冲区 (容器的一个优化措施)
size_t nSize; // 容器已使用的空间大小
};

或者另一个常见的版本:

1
2
3
4
5
6
7
8
9
10
11
union __string_value
{
char local[16]; // 小字符串的本地空间
char* actual; // 容器的堆指针 (只有当字符串大小超过16才有效)
};
struct string
{
__string_value value; // 容器的空间 (容器的一个优化措施)
size_t nSize; // 容器已使用的空间大小
size_t nMaxSize; // 容器已分配的空间大小
};

需要注意的是,在旧标准版本中(C++11之前),由于 C 标准布局类型导致空基类会占用四字节的空间(实际上空基类占用的是一字节,但是被补齐为四字节)。

一个可能的空间分布如下:

image-20220816135930341

当没有空基类优化时,可能的空间分布为:

image-20220816140019397

特征

实际上类在 C++ 中是一种语法糖的表现形式,在底层编译时会被分离为结构体和函数(大概率是内联函数)的形式,一个较为明显的特征点是由于 std::string 的小字符串优化,于是会存在取真实值的时候会进行 std::string.nMaxSize >= 16 的比较。

例如以下这种判断形式:

image-20220816140602051

将其更改得更为明显一些(更改变量类型):

image-20220816141533896