std::basic_string构造函数如何事先知道要保留多少空间

How does the std::basic_string constructor know beforehand how much space to reserve?

本文关键字:保留 多少 空间 先知 basic string 构造函数 std 何事      更新时间:2023-10-16

std::basic_string具有以下构造函数,该构造函数使用s指向的以null结尾的字符串的内容初始化字符串:

std::basic_string(const CharT* s, const Allocator& alloc = Allocator());

但是构造函数如何事先知道在其内部缓冲区中为字符串保留多少空间?

我能想到两种方法:

1)它可以首先遍历整个以null结尾的字符串,直到找到第一个NULL 字符,记住它遍历了多少个字符,并将其用作其内部缓冲区的容量并开始复制。

缺点:它必须读取字符串两次,一次用于计算字符,第二次用于复制字符串。

2)它可以在其内部缓冲区中保留保守的数量,然后开始复制。如果它在缓冲区用完之前命中 NULL 字符,我们就可以了,否则我们需要保留更多空间(再次保留保守数量),然后重复这些步骤。

缺点:如果字符串相当大,则不断重新调整容量的开销可能会变得明显。

那么,一个理智的std::basic_string实现是做什么的(或者这甚至在标准中指定)?

常见的实现会遍历原始字符串来计算长度,然后分配那么多空间。它需要两次执行字符串,但这是一个快速的操作,在某些情况下有硬件支持,即使没有硬件支持操作,与单个内存分配相比,它可能很便宜

第一种方法是答案。根据标准 §21.4.2:

basic_string(const charT* s, const Allocator& a = Allocator());

9 效果:构造类 basic_string 的对象,并从长度为 traits::length(s) 个字符数组中确定其初始字符串值,其第一个元素由 s...

10 备注:使用traits::length() .

GCC 的实现是:

  template<typename _CharT, typename _Traits, typename _Alloc>
    basic_string<_CharT, _Traits, _Alloc>::
    basic_string(const _CharT* __s, const _Alloc& __a)
    : _M_dataplus(_S_construct(__s, __s ? __s + traits_type::length(__s) :
                   __s + npos, __a), __a)
    { }

它使用类似于std::char_traits::lengthtraits_type::length来发现 c 样式零终止字符串的长度。


如果你有巨大的条目字符串来传递函数并且你有它的长度,你可以使用另一个重载来获取大小并且不会再次计算它:

basic_string(const CharT* s, size_type count, ...)

您提到的第二种方法还有另一个缺点,它必须缩小分配内存以停止浪费内存。此操作也很昂贵。

我想不出使用第二种方法的理智实现。某些实现(即Visual C++)确实执行默认初始化,这可能会分配一些最小长度(例如 1 或 16),然后调用 assign ,这将获取字符串的长度,如有必要重新分配,然后复制字符串。

许多(如果不是全部)现代编译器将使用手动调整的汇编语言来获取以 null 结尾的字符串的长度,这通常非常快。正在执行分配-复制-重新分配-复制等...真的,至少在我所知道的所有平台上都是疯狂的。