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

时间:2013-11-04 22:11:14

标签: c++ c++11 stdstring

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

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

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

我可以想到两种方法:

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

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

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

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

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

3 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:4)

第一种方法就是答案。按照标准§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, ...)

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

答案 2 :(得分:1)

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

许多 - 如果不是全部 - 现代编译器将使用手动调整的汇编语言来获取以null结尾的字符串的长度,通常快。做一个allocate-copy-reallocate-copy-etc ......真是疯了,至少在我所知道的所有平台上都是如此。