std::string 是如何实现的?

Posted

技术标签:

【中文标题】std::string 是如何实现的?【英文标题】:How is std::string implemented? 【发布时间】:2010-11-30 18:23:00 【问题描述】:

我很想知道 std::string 是如何实现的,它与 c 字符串有什么不同?如果标准没有指定任何实现,那么任何带有解释的实现都会很好地满足标准给出的字符串要求?

【问题讨论】:

你可能想把源代码放到 gcc 之类的东西上,看看他们是如何实现的。 相关:***.com/questions/3170295/… 【参考方案1】:

an answer on this page 中有一个示例实现。

另外,你可以看看 gcc 的实现,假设你已经安装了 gcc。 If not, you can access their source code via SVN。 std::string 大部分是由basic_string 实现的,所以从那里开始。

另一个可能的信息来源是Watcom's compiler

【讨论】:

【参考方案2】:

std::string 是一个封装某种内部缓冲区并提供操作该缓冲区的方法的类。

C 中的字符串只是一个字符数组

在这里解释 std::string 如何工作的所有细微差别需要太长时间。也许看看 gcc 源代码http://gcc.gnu.org 看看他们是如何做到的。

【讨论】:

【参考方案3】:

这取决于您使用的标准库。

STLPort 例如是一个 C++ 标准库实现,它实现了字符串等。

【讨论】:

【参考方案4】:

几乎我使用过的每个编译器都提供了运行时的源代码 - 因此,无论您使用的是 GCC 还是 MSVC 或其他什么,您都可以查看实现。但是,std::string 的很大一部分或全部将被实现为模板代码,这可能会导致非常难以阅读。

Scott Meyer's book, Effective STL,有一章关于 std::string 实现,是对常见变体的一个不错的概述:“第 15 条:注意string 实现中的变体”。

他谈到了 4 种变体:

引用计数实现的几个变体(通常称为写入时复制) - 当字符串对象被原封不动地复制时,引用计数会增加,但实际的字符串数据不会。两个对象都指向相同的引用计数数据,直到其中一个对象修改它,从而导致数据的“写入时复制”。变化在于存储引用计数、锁等内容的位置。

“短字符串优化”(SSO) 实现。在这个变体中,对象包含通常指向数据的指针、长度、动态分配缓冲区的大小等。但是如果字符串足够短,它将使用该区域来保存字符串,而不是动态分配缓冲区

另外,Herb Sutter's "More Exceptional C++" 有一个附录(附录 A:“不存在的优化(在多线程世界中)”)讨论了为什么在多线程应用程序中由于同步问题,写时复制引用实现经常会出现性能问题。那篇文章也可以在线获得(但我不确定它是否与书中的内容完全相同):

http://www.gotw.ca/publications/optimizations.htm

这两章都值得一读。

【讨论】:

注意(因为链接自最近的问题):有趣的是,在 move 情况下,GCC 的写时复制实现性能优于 VC++ 短字符串优化,因为 move 操作主要受sizeof 对象的影响。 “短字符串优化”显然经常缩写为“SSO”:***.com/questions/10315041/meaning-of-acronym-sso 我同意 Scott Meyers 和 Herb Sutter 的书籍包含有关字符串实现的大量一般信息,但他们没有确切说明哪些实现使用了哪些优化(或非优化)。如果你想知道这里有一个比较:http://info.prelert.com/blog/cpp-stdstring-implementations gcc 实现在这里:gcc.gnu.org/onlinedocs/gcc-4.8.1/libstdc++/api/…【参考方案5】:

字符串的 c++ 解决方案与 c 版本完全不同。第一个也是最重要的区别是,当 c 使用 ASCIIZ 解决方案时,std::string 和 std::wstring 使用两个迭代器(指针)来存储实际的字符串。字符串类的基本用法提供了动态分配的解决方案,因此动态内存处理的 CPU 开销成本使得字符串处理更加舒适。

您可能已经知道,C 不包含任何内置的通用字符串类型,仅通过标准库提供几个字符串操作。 C 和 C++ 之间的主要区别之一是 C++ 提供了封装的功能,因此可以将其视为伪造的泛型类型。

在 C 语言中,如果您想知道字符串的长度,则需要遍历字符串,std::string::size() 成员函数基本上只有一条指令(结束 - 开始)。只要您有内存,您就可以安全地将字符串一个接一个地追加,因此无需担心缓冲区溢出错误(以及漏洞利用),因为如果需要,追加会创建更大的缓冲区。

正如之前有人所说,字符串是从向量功能派生的,以模板化的方式,因此更容易处理多字节字符系统。您可以使用 typedef std::basic_string specific_str_t 定义自己的字符串类型;模板参数中具有任意数据类型的表达式。

我认为双方都有足够的利弊:

C++ 字符串优点: - 在某些情况下更快的迭代(绝对使用大小,它不需要内存中的数据来检查你是否在字符串的末尾,比较两个指针。这可能会对缓存产生影响) - 缓冲区操作包含字符串功能,因此无需担心缓冲区问题。

C++ 字符串缺点: - 由于动态内存分配的东西,基本使用可能会影响性能。 (幸运的是,您可以告诉字符串对象原始缓冲区大小应该是多少,所以除非您超过它,否则它不会从内存中分配动态块) - 与其他语言相比,名称通常很奇怪且不一致。这是任何 stl 东西的坏处,但你可以习惯它,它会让人感觉有点特定的 C++ 风格。 - 模板的大量使用迫使标准库使用基于头文件的解决方案,因此对编译时间有很大影响。

【讨论】:

以上是关于std::string 是如何实现的?的主要内容,如果未能解决你的问题,请参考以下文章

源码分析C++的string的实现

GCC 如何连接多个 C++ std::string 变量?

C++ STL应用与实现5: 如何使用std::array (since C++11)

如何使用模板参数包实现 SFINAE 仅限于少数类型

C++11 中 COW std::string 实现的合法性

如何使用 dtrace 打印 libstdc++ 字符串内容