每次使用循环插入项目时,std :: vector如何重新分配?

时间:2015-11-06 16:19:40

标签: c++ vector

我已经读过std::vector重新分配的工作原理如下:

  

重新分配有多贵?它涉及四个步骤:

     
      
  1. 为所需的新容量分配足够的内存;
  2.   
  3. 将元素从旧内存复制到新内存;
  4.   
  5. 销毁旧记忆中的元素;和
  6.   
  7. 释放旧记忆。
  8.   

因此,使用.reserve()可能是一个好习惯:

std::vector<int> vec;
int unnkownNumberOfElementsToAdd = 30; //it's 30 now, but suppose you don't know

vec.reserve(unnkownNumberOfElementsToAdd);
for(int i=0; i<unnkownNumberOfElementsToAdd; i++ )
{
    vec.push_back(i);
}

因此,每次插入项目时,它都不会重新分配整个矢量。 但有趣的是,如果您 DON&#39; .reserve()并且每次插入vec.size时都打印vec.capacityi,这是输出:

size | capacity
1 1
2 2
3 3
4 4
5 6
6 6
7 9
8 9
9 9
10 13
11 13
12 13
13 13
14 19
15 19
16 19
17 19
18 19
19 19
20 28
21 28
22 28
23 28
24 28
25 28
26 28
27 28
28 28
29 42
30 42

我不知道容量增加是否取决于编译器(我使用旧的VS2003)。如果不是,这个重新分配如何运作?

5 个答案:

答案 0 :(得分:9)

每次添加元素时,

std::vector都不会重新分配。它在容量耗尽时重新分配。当它重新分配时,它不会为另外一个元素分配空间。它通常按当前容量的某个因素分配。我认为VS使用1.5的因子,而其他一些使用2.它必须这样做以确保push_back已经摊销了O(1)复杂度,这是标准的要求。

如果你确切地知道你将在生命周期中向向量添加多少元素,那么保留imo仍然是一个好主意。有些人可能会认为过早优化。但这样做很简单,我认为这不是过早的悲观化。

答案 1 :(得分:1)

通常,每当电流大小超过最大容量时,容量就会增加一个常数因子。但它依赖于编译器,特别是在较小的数字中。初始容量也是如此。例如。 g++将保留8个元素AFAIK的初始大小。

将存储乘以一个因子会导致插入的持续摊销复杂度,但在插入的有效工作和未使用的容量之间需要进行权衡。对于大向量,保留但未使用的容量可能很大(直到使用的常数因子)。这就是为什么这是编译器定义的行为。针对内存受限系统(例如嵌入式系统)进行优化的编译器可能会选择以增加运行时间为代价来减少此因素。

如果您知道大小,只需使用reserve以避免依赖于编译器特定的行为。

答案 2 :(得分:1)

reserve()现在已经过时了,因为矢量重新分配策略适合大多数用途。

但是,它们有缺点。例如,当我处理32位系统时,我在内存中有一个非常大的数据结构(很大,因为它包含很多元素),并且它的一个成员是一个向量。矢量非常短 - 很多都是空的,有些有一两个元素。然而,实现预先分配了32个元素,并导致我的程序耗尽内存(仅仅因为我有那些向量的疯狂数量)。用列表替换向量会降低我的随机访问权限,但允许程序运行。

答案 3 :(得分:1)

使用reserve时真的非常小心。如果在循环中错误地使用它,可以大大增加执行时间,因为强制重新分配是算术而不是几何:

#include <chrono>
#include <iostream>
#include <vector>

void Add2Items(std::vector<int>& vec, bool reserve)
{
    if(reserve)
    {
        // Surely reserving upfront the sapce for the 2 items I'm going to add is a _good thing_
        vec.reserve(vec.size() + 2);
    }
    vec.push_back(42);
    vec.push_back(42);
}

int main()
{
    for(int iter=0; iter<2; ++iter)
    {
        bool reserve = iter ? false : true;

        auto begin = std::chrono::system_clock::now();
        std::vector<int> vec;
        for(int i=0; i<1024 * 64; ++i)
        {
            Add2Items(vec, reserve);
        }
        auto end = std::chrono::system_clock::now();
        std::cout << "Iteration " << iter << " with " << (reserve ? "reserve" : "no reserve") << " took " << std::chrono::duration_cast<std::chrono::milliseconds>(end-begin).count() << "ms" << std::endl;
    }
}

输出

Iteration 0 with reserve took 2432ms
Iteration 1 with no reserve took 133ms

答案 4 :(得分:0)

这是了解矢量如何自动分配的好方法。这当然是按照效率的方式完成的,系统的输出会有所不同,但它会给出一个大致的想法!

#include <iostream>
#include <vector>

int     main(void)
{
    std::vector<int> v;
    int i = 0;
    while (v.size() <= 100)
    {
        std::cout << "Size: " << v.size() << std::endl;
        std::cout << "Capacity: " << v.capacity() << std::endl;
        v.push_back(i++);
    }

}

它通常会打印出这样的内容: 大小:0 容量:0 尺寸:1 容量:1 尺寸:2 容量:2 尺寸:3 容量:4 尺寸:4 容量:4 尺寸:5 容量:8 尺寸:6 容量:8 大小:7 容量:8 大小:8 容量:8 大小:9 容量:16 尺寸:10 容量:16 大小:11 容量:16 尺寸:12 容量:16 大小:13 容量:16 大小:14 容量:16 尺寸:15 容量:16 大小:16 容量:16 大小:17 容量:32 尺寸:18 容量:32 ......