3

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

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

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

我可以想到两种方法:

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

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

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

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

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

4

3 回答 3

6

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

于 2013-11-04T22:22:20.963 回答
4

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

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

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

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)
    { }

它使用traits_type::length类似于std::char_traits::length发现 c 样式以零结尾的字符串的长度。


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

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

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

于 2013-11-04T22:21:57.537 回答
1

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

许多(如果不是全部)现代编译器将使用手动调整的汇编语言来获取以空字符结尾的字符串的长度,这通常非常快。至少在我所知道的所有平台上,执行 allocate-copy-reallocate-copy-etc... 真的很疯狂。

于 2013-11-04T22:40:35.340 回答