写在前面
本笔记介绍了C++11之后的GCC string的核心数据成员,以及短字符串的栈上优化。
C++11之前与之后string的实现完全不同
(这个SO回答写的很好)
简而言之,c++11之前实现的是COW string。C++11之后实现的就是实时拷贝,因为C++11标准规定:不允许[]导致之前的迭代器失效,这就使得COW的string不再符合C++规范了。
在gcc里面可以看到,c++11前后的basic_string的实现是完全分开的,各占了上千行,基本没有公用的部分。
一个重要的区别是,COW 的 basic_string有一个refcount变量,用于引用计数。而C++11 basic_string完全没有。
Short String优化
这个SO回答指出了不同编译器下的Short String优化。
这里仅讨论gcc编译器,clang(libc++)参考 https://joellaity.com/2020/01/31/string.html。原理其实差不多。
gcc中的basic_string有4个数据成员:
- _M_dataplus
- _M_string_length
- _S_local_capacity (不占空间,估计编译时直接把所有 _S_local_capacity替换为了15)
- 匿名union
包含数据成员的代码如下:
105 // Use empty-base optimization: http://www.cantrip.org/emptyopt.html
106 struct _Alloc_hider : allocator_type // TODO check __is_final
107 {
108 _Alloc_hider(pointer __dat, const _Alloc& __a = _Alloc())
109 : allocator_type(__a), _M_p(__dat) { }
110
111 pointer _M_p; // The actual data.
112 };
113
114 _Alloc_hider _M_dataplus;
115 size_type _M_string_length;
116
117 enum { _S_local_capacity = 15 / sizeof(_CharT) };
118
119 union
120 {
121 _CharT _M_local_buf[_S_local_capacity + 1]; //之所以是15 + 1,
//是因为字符串要是null terminated的
122 size_type _M_allocated_capacity;
123 };
124
将数据成员“画”出来:
当string长度小于等于 _S_local_capacity时,使用_M_local_buf,字符串存储在栈上。 当string长度大于是,在堆上分配。
采用这个SO回答的方法,可以清晰的看到在堆上和栈上分配的结果。
#include <string>
#include <new>
#include <cstdio>
#include <cstdlib>
std::size_t allocated = 0;
void* operator new (size_t sz)
{
void* p = std::malloc(sz);
allocated += sz;
return p;
}
void operator delete(void* p) noexcept
{
return std::free(p);
}
int
main()
{
allocated = 0;
std::string s("length is 15 ");
std::printf("stack space = %zu, heap space = %zu, capacity = %zu, size = %zun",
sizeof(s), allocated, s.capacity(), s.size());
}
输出:
stack space = 32, heap space = 0, capacity = 15, size = 15
当字符串长度超过15时: std::string s("length is 16 ");
stack space = 32, heap space = 17, capacity = 16, size = 16
size capacity
size 和 capacity与vector中的含义完全一致。它们一般是相同的,只有当使用push_back. append的时候,会导致跟vector.push_back一样的增长方式。
注意capacity不包括Null ternimator,所以在堆上实际分配的大小为capacity + 1.
vector 与 string的区别
https://stackoverflow.com/a/4557156
关于这一条
swapping basic_string invalidates iterators (enabling small string optimization), swapping vectors doesn't.
这是short string优化造成的: 如果是长字符串,迭代器指向的堆上地址,如果是短字符串,那么迭代器指向的是_M_local_buffer中的某一项。swap会改变_M_local_buffer,所以会使迭代器失效。