在 vector::resize() 和 vector::reserve() 之间选择

Posted

技术标签:

【中文标题】在 vector::resize() 和 vector::reserve() 之间选择【英文标题】:Choice between vector::resize() and vector::reserve() 【发布时间】:2011-09-13 06:38:20 【问题描述】:

我正在为我的 vector 成员变量预分配一些内存。下面的代码是最小的部分

class A 
  vector<string> t_Names;
public:
  A () : t_Names(1000) 
;

现在在某个时间点,如果 t_Names.size() 等于 1000。我打算将大小增加100。然后如果达到1100,再增加100,以此类推。

我的问题是,在 vector::resize()vector::reserve() 之间选择什么。这种场景有没有更好的选择?

编辑:我对t_Names 有某种精确的估计。我估计它在700800 左右。然而,在某些(很少)情况下,它的增长速度可能超过1000

【问题讨论】:

您意识到这样做意味着向量增长不再是摊销的常数时间,并且您失去了使用std::vector 的性能优势之一。 相关,请参阅 Dobbs 博士网站上的 C++ Made Easier: How Vectors Grow。 【参考方案1】:

这两个函数做的事情截然不同!

resize() 方法(并且将参数传递给构造函数等效于该方法)将向向量插入或删除适当数量的元素以使其具有给定大小(它具有可选的第二个参数来指定它们的值)。它将影响size(),迭代将遍历所有这些元素,push_back 将插入它们之后,您可以使用operator[] 直接访问它们。

reserve() 方法仅分配内存,但未初始化。它只影响capacity(),但size() 将保持不变。对象没有任何价值,因为向量中没有添加任何内容。如果您随后插入元素,则不会发生重新分配,因为它是提前完成的,但这是唯一的效果。

所以这取决于你想要什么。如果您想要一个包含 1000 个默认项的数组,请使用 resize()。如果您想要一个希望插入 1000 个项目的数组并希望避免多次分配,请使用 reserve()

编辑: Blastfurnace 的评论让我再次阅读了这个问题并意识到,在你的情况下,正确的答案是不要手动预分配。只需根据需要在末尾插入元素即可。向量将根据需要自动重新分配,并且比提到的手动方式更有效地reserve() 有意义的唯一情况是,当您对所需的总大小有相当精确的估计时,您需要提前轻松获得。

EDIT2: 广告问题编辑:如果您有初步估计,那么reserve() 那个估计。如果这还不够,那就让矢量来做吧。

【讨论】:

“reserve() 有意义的唯一情况是,当您对所需的总大小有相当精确的估计时,您需要提前轻松获得。” - 不完全正确,因为在特定时间自己调用reserve() 有时可能会帮助您管理向量元素的任何指针或迭代器(特别是它们因重新分配而失效)。并不是说这个问题有任何迹象表明这种事情正在发生。确实,您需要知道在需要指针/迭代器期间将添加多少元素的上限。 @Jan:好吧,它是否脆弱取决于您自己维护所需财产的难度。就保留空间而言,x.reserve(x.size() + newdata); vector&lt;int&gt;::iterator special_element = get_special_element(x); for (int i = 0; i &lt; newdata; ++i) if some_function(i, special_element) x.push_back(i); 之类的东西非常强大。我不知道实际会添加多少元素,但我有一个上限。当然,如果有疑问,对于向量,您可以只使用索引而不是迭代器,差异通常可以忽略不计。 您的措辞对于已经知道正确答案的人来说是有意义的,但很容易误导需要提问的人。 “resize()...将在向量中插入给定数量的元素” - 仅在第一次使用时为真 - 它通常插入请求的数字和预先存在的 size() 之间的差异。 “reserve() 方法只分配内存”——它可能会也可能不会分配内存,具体取决于capacity() 是否已经足够,它可能还需要移动元素并释放它们的原始内存。 “想要避免几次分配”和副本等 其实推前预留很重要,一定要用。假设您正在编写某种 3d 模型加载器,并且该模型有大约 15000 个顶点。如果您在加载时尝试 push_back 每个顶点而不首先预先分配它们,这将花费大量时间。我亲身经历过,我尝试加载一个有近 100000 个顶点的汽车 .obj 模型,耗时 30 秒。然后我使用带有 .reserve() 的预分配重构了代码,现在需要 3 秒。只需在代码开头添加一个 .reserve(100000) 即可节省 27 秒。 @deniz 这在 100000 的规模上是微不足道的,但在 100-300 的规模上却不是这样,如果不必要地进行保留可能会造成浪费。【参考方案2】:

resize() 不仅分配内存,它还创建 与您传递给resize() 作为参数的期望 大小一样多的实例。但是reserve() 只分配内存,不创建实例。也就是说,

std::vector<int> v1;
v1.resize(1000); //allocation + instance creation
cout <<(v1.size() == 1000)<< endl;   //prints 1
cout <<(v1.capacity()==1000)<< endl; //prints 1

std::vector<int> v2;
v2.reserve(1000); //only allocation
cout <<(v2.size() == 1000)<< endl;   //prints 0
cout <<(v2.capacity()==1000)<< endl; //prints 1

输出(online demo):

1
1
0
1

所以resize() 可能不是可取的,如果您不想要默认创建的对象。它也会很慢。另外,如果你给它push_back()新元素,向量的size()会进一步增加分配新内存(这也意味着将现有元素移动到新分配的内存空间)。如果您在开始时使用了reserve() 以确保已经分配了足够的内存,那么当您向它push_back() 时,向量的size() 会增加,但它不会再次分配新的内存,直到它运行超出您为其保留的空间

【讨论】:

做了reserve(N)之后,我们就可以无害地使用operator []了。正确吗? 虽然大多数实现将分配您通过reserve 请求的确切数量,但规范只要求它至少分配那么多,因此某些实现可能会四舍五入到某个边界,从而显示比 1000 更高的容量. @iammilind:不,如果索引大于或等于v.size()。注意reserve(N) 不会改变向量的size() @iammilind:不正确。调用reSERVE后,没有添加条目,只获得了足够的内存来添加它们。【参考方案3】:

根据您的描述,您似乎想“保留”向量 t_Names 的分配存储空间。

注意resize 初始化新分配的向量,reserve 只是分配但不构造。因此,“reserve”比“resize”快得多

resize和reserve的区别可以参考文档

【讨论】:

请参阅此处:vector 和 capacity (why?) 感谢添加链接,谢【参考方案4】:

当您不希望在保留时初始化对象时保留。此外,您可能更喜欢在调整大小时在逻辑上区分和跟踪其计数与使用计数。所以界面中存在行为差异 - 向量在保留时将表示相同数量的元素,并且在您的场景中调整大小时将大 100 个元素。

在这种情况下有没有更好的选择?

在对抗默认行为时,这完全取决于您的目标。有些人会喜欢定制的分配器——但我们确实需要更好地了解您在程序中试图解决的问题,以便为您提供良好的建议。

fwiw,许多向量实现在它们必须增长时只会将分配的元素数量增加一倍 - 您是要尽量减少峰值分配大小,还是要为某些无锁程序或其他东西保留足够的空间?

【讨论】:

"当您不希望对象在保留时被初始化时保留。" 正确的表述是当您不希望对象存在时。它不像一个普通可构造类型的未初始化数组,其中对象无法读取但可以分配;相反,只保留内存,但其中不存在任何对象,因此无法使用operator[] 或其他任何方式访问它们。

以上是关于在 vector::resize() 和 vector::reserve() 之间选择的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 C++11 中更改了 std::vector::resize 签名?

std::vector::resize() 与 std::vector::reserve()

如果 std::vector::resize 的 size 参数等于当前大小怎么办?

C++ Vector Resize函数

在c ++ Qt中将二维向量/二维数组显示为简单图形

Verilog常用的数据选择语句vect[a +: b]或vect [a -: b]