有两种方法可以定义std :: vector(我知道):
std::vector<int> vectorOne;
和
std::vector<int> vectorTwo(300);
因此,如果我没有定义第一个并用300个int填充它,那么它必须重新分配内存来存储这些内容。这意味着它不会是例如地址0x0到0x300,但可能会在中间分配内存,因为它必须在之后重新分配,但第二个矢量已经为它们保留了那些地址,因此中间没有空间。
这是否会影响性能,我怎么能这样做呢?
答案 0 :(得分:3)
std::vector
始终将其数据存储在连续的内存块中。这意味着当您添加项目时,它必须尝试增加其使用的内存范围。如果向量后面的内存中有其他内容,则需要在内存中的其他位置找到正确大小的空闲块,并将所有旧数据+新数据复制到其中。
就时间而言,这是一项相当昂贵的操作,因此它会尝试通过分配比您需要的块稍大的块来缓解。这允许您在整个重新分配和移动操作发生之前添加多个项目。
Vector有两个属性:size
和capacity
。前者是实际拥有多少元素,后者是总共保留了多少个元素。例如,如果您有一个带有size() == 10
和capacity() == 18
的向量,则意味着您可以在需要重新分配之前再添加8个元素。
容量如何以及何时准确增加,取决于STL版本的实施者。您可以使用以下测试来测试计算机上发生的情况:
#include <iostream>
#include <vector>
int main() {
using std::cout;
using std::vector;
// Create a vector with values 1 .. 10
vector<int> v(10);
std::cout << "v has size " << v.size() << " and capacity " << v.capacity() << "\n";
// Now add 90 values, and print the size and capacity after each insert
for(int i = 11; i <= 100; ++i)
{
v.push_back(i);
std::cout << "v has size " << v.size() << " and capacity " << v.capacity()
<< ". Memory range: " << &v.front() << " -- " << &v.back() << "\n";
}
return 0;
}
我在IDEOne上运行它并得到以下输出:
v has size 10 and capacity 10
v has size 11 and capacity 20. Memory range: 0x9899a40 -- 0x9899a68
v has size 12 and capacity 20. Memory range: 0x9899a40 -- 0x9899a6c
v has size 13 and capacity 20. Memory range: 0x9899a40 -- 0x9899a70
...
v has size 20 and capacity 20. Memory range: 0x9899a40 -- 0x9899a8c
v has size 21 and capacity 40. Memory range: 0x9899a98 -- 0x9899ae8
...
v has size 40 and capacity 40. Memory range: 0x9899a98 -- 0x9899b34
v has size 41 and capacity 80. Memory range: 0x9899b40 -- 0x9899be0
您可以看到容量增加和重新分配正在发生,并且您还看到此特定编译器选择在每次达到限制时将容量加倍。
在某些系统上,算法会更加细微,随着插入更多项目而增长得更快(因此,如果向量较小,则会浪费很少的空间,但如果注意到您在其中插入了大量项目,则会分配更多内容避免不得不经常增加容量。)
PS:请注意设置矢量的size
和capacity
之间的区别。
vector<int> v(10);
将创建一个capacity
至少为10且size() == 10
的向量。如果您打印v
的内容,您会看到它包含
0 0 0 0 0 0 0 0 0 0
即 10个整数及其默认值。您推入的下一个元素可能(并且可能将)导致重新分配。另一方面,
vector<int> v();
v.reserve(10);
将创建一个空向量,但其初始容量设置为10而不是默认值(可能为1)。您可以确定您推入的前10个元素不会导致分配(而可能将会但不一定,因为reserve
可能实际上将容量设置为 比您要求的更多。
答案 1 :(得分:1)
你应该使用reserve()方法:
std::vector<int> vec;
vec.reserve(300);
assert(vec.size() == 0); // But memory is allocated
这解决了这个问题。
在您的示例中,它会极大地影响性能。你可以预料,当你向量溢出时,它会使分配的内存加倍。因此,如果将push_back()转换为向量N次(并且您没有调用“reserve()”),则可以预期O(logN)重新分配,每次重新分配都会导致复制所有值。因此,总复杂度预计为O(N * logN),尽管C ++标准没有规定。
答案 2 :(得分:0)
差异可能很大,因为如果数据在内存中不相邻,则可能必须从主内存中提取数据,这比l1缓存提取慢200倍。这不会发生在矢量中,因为矢量中的数据必须是相邻的。
请参阅https://www.youtube.com/watch?v=YQs6IC-vgmo
使用std :: vector :: reserve,可以避免重新分配事件。 C ++&#39; chrono&#39;标题具有良好的时间效用,用于测量时间差,以高分辨率标记。