13

我对 C++ 很陌生,但我知道你不能像 std::string 类似乎让你那样随意使用内存。例如:

std::string f = "asdf";
f += "fdsa";

字符串类如何处理越来越大的问题?我假设它分配了默认的内存量,如果它需要更多,它new会占用更大的内存块并将其自身复制到该内存块。但是,每次需要调整大小时都必须复制整个字符串,这不是非常低效吗?我真的想不出另一种方法可以做到(但显然有人做到了)。

就此而言,所有 stdlib 类(如向量、队列、堆栈等)如何如此透明地处理增长和收缩?

4

3 回答 3

8

通常,有一个加倍算法。换句话说,当它填满当前缓冲区时,它会分配一个两倍大的新缓冲区,然后将当前数据复制过来。与按单个分配块增长的替代方案相比,这导致分配/复制操作更少。

于 2010-08-24T14:36:37.887 回答
8

您的分析是正确的 -每次需要调整大小时都复制字符串效率低下这就是为什么常见的建议不鼓励使用模式。使用字符串的reserve函数要求它为您打算在其中存储的内容分配足够的内存。然后进一步的操作将填充该内存。(但如果你的提示太小,字符串也会自动增长。)

容器通常还会尝试通过分配比它们需要的更多的内存来减轻频繁重新分配的影响。一种常见的算法是,当一个字符串发现它的空间不足时,它将其缓冲区大小加倍,而不是仅仅分配保存新值所需的最小值。如果字符串一次增长一个字符,这种加倍算法将时间复杂度降低到摊销线性时间(而不是二次时间)。它还降低了程序对内存碎片的敏感性。

于 2010-08-24T14:50:54.183 回答
3

虽然我不知道 std::string 的确切实现,但大多数需要处理动态内存增长的数据结构都是按照你说的去做 - 分配默认数量的内存,如果需要更多,则创建一个更大的块并复制自己。

解决明显的低效率问题的方法是分配比您需要的更多的内存。已用内存与向量/字符串/列表/等的总内存之比通常称为负载因子(也用于哈希表,含义略有不同)。通常它是 1:2 的比例 - 也就是说,您分配两倍的内存。当空间不足时,您分配两倍于当前数量的新内存量并使用它。这意味着随着时间的推移,如果您继续向向量/字符串/等添加内容,您需要越来越少地复制项目(因为内存创建是指数级的,并且您插入新项目当然是线性的),所以这种内存处理方法所花费的时间并不像你想象的那么大。按摊销分析原理,然后您可以看到m使用此方法将项目插入向量/字符串/列表只是 m 的 Big-Theta,而不是 m 2

于 2010-08-24T14:47:17.007 回答