我发现即使调整大小超过当前大小的一个元素,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。
我想知道为什么实施对resize
和reserve
使用不同的扩展策略。它似乎不一致,我希望在这里有相同的行为。
我只是添加了一个指向我的问题动机的链接,其中报告了对效果的影响: 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_back
或insert
(§23.3.6.5)。
resize
。它列在§23.3.6.3vector
容量部分中。而且,resize
没有复杂性要求。
但是,在vector
概述部分(§23.3.6.1)中,写有:
它(
vector
)在末尾支持(分期)常量时间插入和擦除操作
问题是resize(size()+1)
是否被视为“插入结尾”。
答案 0 :(得分:17)
据我所知,resize
和reserve
都不需要具有已演示的行为。然而,两者都允许这样的行为,尽管两者都可以分配确切的数量,并且就标准而言,两者都可以倍增先前的分配。
每种分配策略都有其优点。分配精确数量的优点是,当事先知道最大分配时,它没有内存开销。乘法的优点是,当与末端插入操作混合时,它保持恒定的分摊属性。
测试实现选择的方法具有以下优点:在调整大小时它允许两种策略。要使用一种策略,可以预留然后调整大小。要使用另一个,只需调整大小。当然,人们必须意识到利用这一点的未指明行为。这种优势可能是也可能不是选择这些实现背后的原因。
有人可能认为它是标准中规定的向量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
时,除了您传递保留项目外,将只有一个分配而没有内存副本。
当您告诉确切的保留计数时,您会获得所要求的确切记忆。当你只是添加元素时(即使调整大小,你也不能说你不会添加更多项目。