为什么std :: vector保留不会“加倍”它的容量,而resize呢?

时间:2018-01-31 08:38:45

标签: c++ vector resize capacity

我发现即使调整大小超过当前大小的一个元素,std::vector<T>::resize“也会”增加“其容量:

std::vector<int> v(50);
v.resize(51);
std::cout << v.capacity() << std::endl;

该程序使用GCC和Clang输出100,使用Visual C ++输出75。但是,当我从resize切换到reserve时:

std::vector<int> v(50);
v.reserve(51);
std::cout << v.capacity() << std::endl;

所有三个编译器的输出为51。

我想知道为什么实施对resizereserve使用不同的扩展策略。它似乎不一致,我希望在这里有相同的行为。

我只是添加了一个指向我的问题动机的链接,其中报告了对效果的影响: Why are C++ STL vectors 1000x slower when doing many reserves?

添加C ++ 11 Standard中的引用以阐明reserve的要求; §23.3.6.3(2):

  

reserve()之后,capacity() 大于或等于,如果重新分配发生在reserve的参数...

一些额外的想法:来自C ++ 11标准:

  

复杂性:插入的元素数量加上到向量末尾的距离是复杂的。

有效地,这意味着在末尾插入单个元素的常数(摊销)复杂性。但是,这仅适用于矢量修饰符,例如push_backinsert(§23.3.6.5)。

修饰符中未列出

resize。它列在§23.3.6.3vector容量部分中。而且,resize没有复杂性要求。

但是,在vector概述部分(§23.3.6.1)中,写有:

  

它( vector )在末尾支持(分期)常量时间插入和擦除操作

问题是resize(size()+1)是否被视为“插入结尾”

5 个答案:

答案 0 :(得分:17)

据我所知,resizereserve都不需要具有已演示的行为。然而,两者都允许这样的行为,尽管两者都可以分配确切的数量,并且就标准而言,两者都可以倍增先前的分配。

每种分配策略都有其优点。分配精确数量的优点是,当事先知道最大分配时,它没有内存开销。乘法的优点是,当与末端插入操作混合时,它保持恒定的分摊属性。

测试实现选择的方法具有以下优点:在调整大小时它允许两种策略。要使用一种策略,可以预留然后调整大小。要使用另一个,只需调整大小。当然,人们必须意识到利用这一点的未指明行为。这种优势可能是也可能不是选择这些实现背后的原因。

有人可能认为它是标准中规定的向量API的失败,表达了预期的重新分配行为是不可能的(以标准保证的方式)。

答案 1 :(得分:9)

当您resize超过容量时,您已经&#34;演示&#34;你不想保留适当的容量。另一方面,如果您使用reserve,则明确要求正确的容量。如果reserve使用与resize相同的策略,则无法保留适当的金额。

在这种意义上,没有resize的{​​{1}}适用于懒惰的,或者您不知道确切的预留金额。如果您知道需要什么容量,请致电reserve。这是两种不同的情景。

PS:正如StoryTeller指出的那样,reserve也不需要保留按照标准要求的确切金额。尽管如此,我认为我的主要观点仍然存在:reserve(没有resize)和reserve适用于不同的场景,你要么暗示要保留多少,要么不要# 39;关心实际容量,只想让容器的大小符合你的要求。

答案 2 :(得分:6)

为什么你会期望它们的行为相同? reserve用于预分配稍后将使用的空间,期望用户对容器的预期最终大小有一个合适的处理。 resize只是一个正常的分配,因此它遵循几何增加容器分配空间的正常,速度有效的方法。

容器通过乘法步骤增加大小,以减少所需的分配数量,从而保持速度并减少内存碎片。加倍是最常见的,但是一些实现使用1.5的步骤(例如MSVC),其为每个容器内的较低浪费空间交换增加的分配。

但是,如果用户已经告诉图书馆他们认为容器会有多大 - 通过导致reserve - 没有必要分配多余的空间,他们可以信任用户用它调用它正确的号码。 reserve具有异常行为,而非resize

答案 3 :(得分:6)

resize需要遵循指数重新分配策略来实现其复杂性保证(元素数量插入的线性)。这可以通过考虑resize(size() + 1)需要具有分摊的常数复杂度来看出,因此必须遵循指数增长,原因与push_back(摊销常数复杂度)必须呈指数增长的原因相同。

允许reserve的实现遵循它喜欢的任何分配策略,因为它唯一的复杂性要求是它与 present 元素的数量呈线性关系。但是,如果实施例如是在用户确切知道需要多少内存的情况下,如果用户开始依赖这种行为,那么这将是空间效率低(并且令人惊讶),这将是空间效率低(并且令人惊讶)。在没有空间效率低的情况下,可以更好地运用标准中的自由度。如果分配器以该粒度运行,则将分配四舍五入为字大小。

答案 4 :(得分:1)

reserve更改了容量,而resize更改了size

capacity是容器当前为其分配空间的元素数。

size是容器中元素的数量。

分配空矢量时,您将获得默认的capacity(AKA空间)。大小仍为0,当您向向量中添加元素时,其大小会增加。当size等于容量并且你添加更多项时,容量必须增长(通常是自身加倍)。

向量的问题在于它确保了顺序内存,这意味着每个新的分配增长还需要先前分配给新分配的副本,以防旧分配的内存区域中没有新分配大小的空间

如果您知道向量中的最大元素,reserve可以提供帮助。当您使用reserve时,除了您传递保留项目外,将只有一个分配而没有内存副本。

当您告诉确切的保留计数时,您会获得所要求的确切记忆。当你只是添加元素时(即使调整大小,你也不能说你不会添加更多项目。