C++ - 字符串容量模式

Posted

技术标签:

【中文标题】C++ - 字符串容量模式【英文标题】:C++ - String capacity pattern 【发布时间】:2021-01-14 16:32:27 【问题描述】:

我注意到 C++ 中的字符串容量遵循以下模式:

初始字符串大小为 15 对于任何大于特定大小“块”的字符串,容量会加倍。

以下是长度不超过 500 的字符串的字符串容量:

15
30
60
120
240
480
960

通过以下 C++ 程序发现容量:

#include <iostream>
#include <vector>

using namespace std;

string getstr(int len)  
    string s = "";
    for (int i=0; i<len; i++) 
        s.append("1");
    
    return s;


int main() 
    vector<int> capacities;
    int prevcap;
    for (int i=0; i<500; i++) 
        int cap = getstr(i).capacity();
        if (cap > prevcap) 
            capacities.push_back(cap);
            prevcap = cap;
        
    
    for (int i : capacities) 
        cout << i << endl;
    

选择这个算法的逻辑是什么?这些数字(这里是 15 和 2)是否有任何意义,或者它们是随机选择的?另外,这个算法是否因编译器而异? (这是在 Ubuntu 16.04 上使用 g++ 5.4.0 编译和测试的)任何见解都值得赞赏。

【问题讨论】:

这取决于实现。在我的编译器 (clang 12.0.0) 上,我得到:22 47 95 191 383 767。我认为这里没有任何特定的模式。从您的观察来看,gcc 似乎每次只是将容量翻倍。 旁注:里面应该有一个#include &lt;string&gt; 我的猜测是最初的 15 来自小字符串优化 (SSO),并且使用了加倍,因为它是廉价的操作(只是位移)。虽然我不是图书馆设计师 保证摊销 O(1) push_back 没有任何魔法.. 编译器在如何实现任何增长算法方面都有余地,但如果你看的话,它基本上是为一些字符分配,当你需要更多时加倍。这是增长和所需分配数量之间的公平平衡。 【参考方案1】:

加倍是一种众所周知的方法。它摊销重新分配的成本,使push_back 成为一个恒定时间操作(因为它是必需的)。添加固定大小的“明显”替代方案将使push_back 成为线性时间操作。不过其他模式也是可能的,理论上任何乘法增加都可以,我曾经读过一篇文章,主张每次增加的容量都应该取自斐波那契数列中的下一项。

我想选择 15 的初始大小是考虑到短字符串优化 (SSO)。使用 SSO,字符串数据存储在字符串对象本身中,而不是单独分配的内存中。我想 15 是这个特定实现中可以容纳的最大短字符串。知道sizeof(std::string) 是什么可能会对此有所了解。

【讨论】:

“我曾经读过一篇文章,主张每次增加的容量都应该从斐波那契数列的下一个术语中获取”——可惜没有 C++ 标准库供应商听过这个极好的建议。 @Bathsheba 我不记得确切建议的原因,这与释放内存时堆中留下的“洞”有关,但我确实记得认为作者有一个相当典型程序中如何使用内存的理想化视图。我不知道有任何真实世界的测试。

以上是关于C++ - 字符串容量模式的主要内容,如果未能解决你的问题,请参考以下文章

初识C++中的string(几种遍历方式基本函数使用)

C++进阶---Map和Set使用及模拟实现

C++ - 如何在字符串中提取有效字符串?

在 C++ 中查找传入字符串中的模式(包括像°这样的特殊字符)

C++——类的模拟实现

如何从 C++ 中的字符串中搜索和修改被某些模式包围的数字? [关闭]