除了标准将其定义为连续的事实之外,为什么std :: vector是连续的?
如果空间不足,则需要重新分配新块并将旧块复制到新块,然后再继续。
如果不连续怎么办?当存储填满时,它只会分配一个新块并保留旧块。通过迭代器访问时,它会做简单的>,<检查索引所在的块并返回它。这样,每次空间用完时都不需要复制数组。
这会真的有用吗?或者我错过了什么?
答案 0 :(得分:23)
如果std::vector
不能保证连续性,那么就会发明一个新的容器。
连续性保证使得与期望连续数组的现有代码交互操作变得更容易,并且由于它是缓存友好的,因此也提供了非常好的性能。 (因此,在中间插入/删除对于中等大小来说非常快。)
在扩展上复制数组的成本非常低廉 - 如果你一次向一个向量追加一百万个元素,每个元素平均会被复制一次。
答案 1 :(得分:13)
标准C ++库也定义了一个非连续的类似数组的容器:std::deque<T>
。 std::deque<T>
上的迭代比迭代std::vector<T>
要慢得多。如果操作相当简单,它可能会慢5倍:这是我在比较累积整数序列时得到的实际时间。这是您为非连续表示支付的费用!
这种相当陡峭的减速的原因是gcc知道如何在std::vector<int>
而不是std::deque<int>
上对循环进行矢量化。即使没有矢量化,迭代也会慢约30%。也就是说,std::vector<T>
重新分配的相当小的成本实际上并不重要!
答案 2 :(得分:10)
这有几个原因:
首先,由于两个因素,对连续容器的迭代要比非连续容器快得多:第一个是分支预测 - 处理器不需要丢弃它的管道每次读完一个子容器,管道重置次数越少意味着代码越快。第二个是完全缓存一个连续的内存块比一堆各种小块容易得多,这使得你的阵列更有可能被整体缓存。
其次,有很多C ++代码必须与C代码进行交互,并且许多代码在接收数组/缓冲区时会期望连续的内存空间,因为这是最少的数据结构实现独立的方式来做到这一点。当您与不断期望缓冲区/数组的代码进行交互时,将std::deque
转换为数组的开销与std::vector
到数组的实际瞬时传递(可能是基本上只是给出一个指向内部数组的指针。)
所有这一切都证明存在一个连续的容器。正如其他人所说,当你不需要快速迭代或内存连续时,你总是可以使用std::deque
。
答案 3 :(得分:9)
通过使std::vector
连续,它可以被视为一个数组。但是,它也可以调整大小。它的大小可以在运行时定义,而不是编译时。此外,矢量可用于为需要缓冲区的函数分配内存。这样做的好处是当vector
超出范围时,内存将被释放。例如,使用ReadFile
时,可以使用向量来创建缓冲区。:
unsigned int bytesRead = 0;
std::vector<char> buffer(fileSize);
// open file, etc.
ReadFile(hFileIn, buffer.data(), buffer.size(), &bytesRead, nullptr);
请注意data
是C ++ 11中的新增功能。在较旧的代码中,您可能会看到等效的&(buffer.at(0))
或&(buffer[0])
,它返回第一个元素的地址。
std::deque
更适合您所描述的内容。
答案 4 :(得分:3)
作为其他答案的补充(它们非常完整),有一种情况是你确实喜欢向量不连续:当你需要同时调整向量的大小时。这就是英特尔线程构建模块提供tbb :: concurrent_vector的原因,这或多或少就是你所说的你想要的
“当存储填满时,它只会分配一个新块并保留旧块。当通过迭代器访问时,它会做简单的&gt;,&lt;检查索引所在的块并返回它“。
然后,tbb :: concurrent_vector和std :: vector之间的比较将使您更好地理解连续内存的优点(速度)和缺点(不能同时增长std :: vector)。我希望tbb :: concurrent_vector比std :: deque更好地优化,这就是为什么tbb :: concurrent_vector vs std :: vector是一个更公平的比较。