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


151

我正在为我的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


34
您意识到这样做意味着向量增长不再按固定时间摊销,并且您失去了使用的性能优势之一std::vector
高炉2011年

1
相关信息,请参见Dobbs博士网站上的C ++变得更容易:向量如何增长
jww

Answers:


262

这两个功能做的事情大不相同!

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

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

因此,这取决于您想要什么。如果要包含1000个默认项目的数组,请使用resize()。如果您希望向其插入1000个项目的数组,并且希望避免几个分配,请使用reserve()

编辑:高炉的评论使我再次阅读了该问题并意识到,在您的情况下,正确的答案是手动进行预分配。只需根据需要在最后插入元素。矢量将根据需要自动重新分配,并且比上述手动方式有效。唯一reserve()有意义的情况是,当您对总大小有合理精确的估计时,您将需要提前轻松获得。

EDIT2:广告问题编辑:如果您有初始估算,那么reserve()该估算。如果结果还不够,那就让向量做它的事情。


我已经编辑了问题。我确实有一些估计vector
iammilind

3
@Jan:好吧,这取决于您使自己维护所需属性的难易程度。x.reserve(x.size() + newdata); vector<int>::iterator special_element = get_special_element(x); for (int i = 0; i < newdata; ++i) { if some_function(i, special_element) x.push_back(i); }就保留空间而言,类似的功能非常强大。我不知道实际上会添加多少个元素,但是我有一个上限。当然,如果有疑问,对于向量,您可以只使用索引而不是迭代器,通常差异可以忽略不计。
史蒂夫·杰索普

4
您的措辞对已经知道正确答案的人来说是有道理的,但很容易误导需要提出问题的人。“ resize()...将在向量中插入给定数量的元素”-仅在第一次使用时才为true-通常会在请求的数量和预先存在的之间插入差异size()。“ reserve()方法仅分配内存”-它是否分配内存取决于是否capacity()已经足够,它可能还需要移动元素并释放其原始内存。“想要避免分配两次”和副本等
Tony Delroy

19
实际上,在推送之前进行预留至关重要,必须加以使用。假设您正在编码某种3D模型加载器,并且该模型具有15000个顶点。如果您尝试在加载时推回每个顶点而不先预先分配它们,则将花费大量时间。我个人经历过,我尝试加载具有近100000个顶点的car .obj模型,这花了30秒。然后,我使用.reserve()的预分配重构了代码,现在需要3秒钟。只需在代码开头放置.reserve(100000)即可节省27秒。
deniz

1
@deniz在100000规模上确实是微不足道的,但在100-300规模上却并非如此,在这种情况下,如果不必要地进行预订会很浪费。
deworde

30

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

输出(在线演示):

1
1
0
1

resize()如果您不希望使用默认创建的对象,则可能不希望如此。也会很慢。此外,如果您push_back()向其添加了新元素,则size()向量的分配通过分配新内存而进一步增加(这也意味着将现有元素移动到新分配的内存空间中)。如果您reserve()一开始就使用来确保已经有足够的已分配内存,则size()在使用向量时,向量的会增加push_back()但是直到用完为它保留的空间时,它才会再次分配新的内存


6
完成后reserve(N),我们可以operator []无害使用。对吗
iammilind

2
虽然大多数实现将分配您请求的确切数额reserve,该规范只要求它分配至少多,所以一些实现可能四舍五入到一些边界,因此比1000表现出较高的容量
扬邬达克

16
@iammilind:否,如果索引大于或等于v.size()。请注意,向量reserve(N)不变size()
Nawaz

5
@iammilind:不正确。调用reSERVE之后,不添加任何条目,仅获得足够的内存来添加它们。
Jan Hudec

2

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

请注意,resize初始化reserve仅分配但不构造的新分配的向量。因此,“储备” 要快得多比“调整大小”

您可以参考有关调整大小保留空间差异的文档


1
请改为参考这里:矢量容量 为什么?
sehe

1
感谢您添加链接,sehe
浸入

2

当您不想在保留时初始化对象时,保留。同样,在调整大小时,您可能更喜欢在逻辑上区分和跟踪其计数与使用计数。因此界面上的行为有所不同-向量在保留时将表示相同数量的元素,在您的方案中调整大小时将增大100个元素。

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

与默认行为作斗争时,这完全取决于您的目标。有些人会喜欢自定义的分配器-但我们确实需要一个更好的主意,以了解您要在程序中尝试解决的问题,以便为您提供良好的建议。

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


当您不希望在保留对象时初始化对象时,保留。 ”正确的表述是当您不希望对象存在时。这不像一个普通的可构造类型的未初始化数组,在该数组中无法读取对象但可以将其分配给它们。而是仅保留内存,但其中不存在任何对象,因此无法使用operator[]或任何方式访问它们。
underscore_d
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.