预分配向量是否更有效? < C++ >第四版,由Stanley B. Lippman、Josee Lajoie和Barbara E. Moo提出:

预分配向量是否更有效? < C++ >第四版,由Stanley B. Lippman、Josee Lajoie和Barbara E. Moo提出:,c++,stl,C++,Stl,因为向量的生长效率很高,所以通常最好让向量 随着元素值的变化,通过向其中动态添加元素进行增长 知道 及 习惯于使用c或java的读者可能会想到这一点,因为vector 元素是连续存储的,最好预先分配 向量的预期大小。事实恰恰相反 及 尽管我们可以在向量中预先分配给定数量的元素, 通常,定义一个空向量并加上 与之相关的元素 假设这是正确的(作者是有信誉的,他们是一个共同作者的C++本身),那么谁能给我一个证明这个陈述的案例,并解释为什么? < P>可以。这在很大程度上取决于元素是什么,复制或构造它

因为向量的生长效率很高,所以通常最好让向量 随着元素值的变化,通过向其中动态添加元素进行增长 知道

习惯于使用c或java的读者可能会想到这一点,因为vector 元素是连续存储的,最好预先分配 向量的预期大小。事实恰恰相反

尽管我们可以在向量中预先分配给定数量的元素, 通常,定义一个空向量并加上 与之相关的元素

假设这是正确的(作者是有信誉的,他们是一个共同作者的C++本身),那么谁能给我一个证明这个陈述的案例,并解释为什么?

< P>可以。这在很大程度上取决于元素是什么,复制或构造它们需要多少工作,以及有多少元素

如果您预先分配了一个向量,那么最终将调用每个元素的默认构造函数来生成空元素,然后在空间上复制该项。如果您添加元素,它可以在适当的位置复制或构造元素,这可能更有效。

这取决于具体情况

如果您不知道最终的大小,那么让向量使用其分配方案进行分配(通常每次都加倍,或者大约加倍)。这样可以避免为每个元素重新分配:

std::vector<int> v;

// good:
for (/* populate v */) // unknown number of iterations
{
    v.push_back(i); // possible reallocation, but not often
}

// bad:
for (/* populate v */) // unknown number of iterations
{
    v.reserve(v.size() + 1); // definite reallocation, every time
    v.push_back(i); // (no reallocation)
}
std::vector v;
//好:
对于(/*填充v*/)//未知的迭代次数
{
v、 向后推(i);//可能的重新分配,但不经常
}
//坏的:
对于(/*填充v*/)//未知的迭代次数
{
v、 reserve(v.size()+1);//每次一定要重新分配
v、 推回(i);/(无再分配)
}
但如果您提前知道您不会重新分配,那么请预先分配:

std::vector<int> v;

// good:
v.reserve(10); 
for (/* populate v */) // only 10 iterations (for example)
{
    v.push_back(i); // no reallocations
}

// not bad, but not the best:
for (/* populate v */) // only 10 iterations (for example)
{
    v.push_back(i); // possible reallocation, but not often (but more than needed!)
}
std::vector v;
//好:
v、 储备(10);
对于(/*填充v*/)//仅10次迭代(例如)
{
v、 向后推(i);//不重新分配
}
//不错,但不是最好的:
对于(/*填充v*/)//仅10次迭代(例如)
{
v、 向后推(i);//可能的重新分配,但不经常(但超出需要!)
}

我给这个简单的例子计时:

#include<iostream>
#include<vector>

int main() {

    int limit = 100 * 1000 * 1000;
    std::vector<long> my_vec;
    my_vec.reserve(limit); // comment out this line to not preallocate

    for (int i=0; i < limit; i++) {
        my_vec.push_back(i);
    }

    long my_sum = 0;
    for (int i=0; i < limit; i++) {
        my_sum += my_vec[i];
    }

    std::cout << my_sum << std::endl;
    return 0;
}
并发现差异很大:

无预分配:

real    0m3.366s
user    0m1.656s
sys     0m1.660s
real    0m1.688s
user    0m0.732s
sys     0m0.936s
预分配:

real    0m3.366s
user    0m1.656s
sys     0m1.660s
real    0m1.688s
user    0m0.732s
sys     0m0.936s
我的结论是:如果构建一个向量是程序的重要组成部分,那么预先分配效率是有意义的。然而,反复构建一个更大的向量是不可能的,因此它很少是一个瓶颈。但是,使用
reserve()
除了预分配之外还有其他优点

C++编程语言中的Bjarne Stroustrup(第四加法)有这样的说法:

我过去在读一本书时,对使用
reserve()
很小心 矢量。我惊讶地发现,对于我所有的用途, 调用
reserve()。默认值
增长战略和我的估计一样有效,所以我停止了
尝试使用
reserve()
提高性能。相反,我用它来 提高重新分配延迟的可预测性并防止 指针和迭代器无效


预分配不需要默认构造<代码>v.reserve(n);//不构造任何内容
但是,如果使用
reserve
,它不会预先进行任何构造,而只进行分配。这可能更好。@GManNickG:我认为这句话的意思是“预分配元素”而不是“预分配元素空间”,它指的是
调整大小
(或者取一个大小的构造函数),而不是
保留
。这里有相当多的语义歧义,所以我可以看出这一点。不过,我认为更重要的是实际应该做什么,而不是太在意作者的话的字面意思。为了澄清,通过“预分配”,鉴于上下文,我认为它指的是向量svec(10,“hi”),我会在我的原始问题上添加一些上下文,但我认为这意味着即使事先知道大小,让向量增长通常更有效。@dangerousdave:好的,但如果你事先知道大小,只需分配一次就可以了。让它增长并不是更好的办法,因为它经常会进行不必要的分配。大多数时候,这种差异会很小,以至于没有人会注意到。那么写更少的代码就更有效率了。@BoPersson:我想那不是真的。内存分配通常不是轻量级的,通过重新分配所做的工作确实是累加的,我在自己的应用程序中看到了这一点。当然,我假设我们谈论的是“真实”数量的数据,而不是微小的沙盒向量。@Boperson:我也是“不进行预成熟优化”人群的一部分,但我之所以在这一人群中,是因为人们经常浪费时间试图进行甚至不在正确位置的优化。如果进行这些优化不花时间,那么我不会阻止它们。这种优化不需要时间,我希望任何有能力的程序员在可能的情况下预先分配一个缓冲区到它所需的大小,这实际上是一行代码。这就像选择正确的排序实现,一个常规的设计决策,而不是试图优化
i++