c++ string 堆还是栈_C++ GCC STL std::string实现 (短字符串优化)

写在前面

本笔记介绍了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 

将数据成员“画”出来:

0a6ccf73dc258423cc504e0af7928e00.png

当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,所以会使迭代器失效。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值