我正在使用vector来管理我的大型结构数据。但突然间,当发现vector
源代码时,我很惊讶地看到下面的代码:
inline void push_back(const _Ty& _X)
{insert(end(), _X); }
//...
void insert(iterator _P, size_type _M, const _Ty& _X)
{
//////////////////////////////////////////////////////////////
iterator _S = allocator.allocate(_N, (void *)0);
iterator _Q = _Ucopy(_First, _P, _S);
_Ufill(_Q, _M, _X);
_Ucopy(_P, _Last, _Q + _M);
_Destroy(_First, _Last);
allocator.deallocate(_First, _End - _First);
//////////////////////////////////////////////////////////////
}
这是“销毁”的片段代码,然后重新分配其整个矢量数据。它太烦人了,因为我的结构有很大的尺寸,矢量必须管理数百个元素,而我只使用vector::operator []
和vector::push_back()
,尤其是 pushing back
占用我的大部分时间(这很费时间)。在我的情况下,是否有更好的容器可以比std::vector
更快地执行,而我试图谷歌但没有运气?
答案 0 :(得分:6)
每次向量超过其当前容量时,只发生一次allocate-copy-delete(或C ++ 11中的allocate-move-delete)。每次重新分配时,容量都会增加一倍。这会随着时间的推移而平均,因此push_back()
的摊销复杂度是不变的。
您可以使用其reserve()
成员函数预先分配向量的容量。
答案 1 :(得分:2)
在添加元素之前,reserving所需的所有空间会解决您的问题吗?
答案 2 :(得分:0)
仅当您的矢量大小超过其容量时才会重新分配。如果您事先知道(甚至粗略地)您的向量将包含多少项,您可以使用其reserve成员预先分配足够的容量。您也可以以受控方式自行触发重新分配,即使在填充向量时,也可以减少调用次数,从而获得更好的性能。
现在,如果你想要保证恒定时间的插入,有一些容器可以用于那个但是需要权衡(例如。std::list
将分配大量的小内存块,这可能最终不会更快您当前的向量使用,因为new
非常慢,并且内存使用率也会更加重要,并且您将失去随机访问权限,但确保每次插入将花费大致相同的时间其他)。
答案 3 :(得分:0)
如果您知道最终数据大小,可以使用reserve
预分配向量所需的内存。这将删除所有重新分配和复制。
如果您不知道确切的大小,请根据您对传入数据的了解做出有根据的猜测。
答案 4 :(得分:0)
一个向量是一个动态扩展的容器,但它的内容需要在一个连续的内存块中(我记得,这在前C ++ 11中没有指定,但每个人都这样做)。因此,如果你的向量已经拥有8个元素,并且向量的当前容量是8个元素,那么当你push_back第9个元素时,容器必须为更大的容量分配新的空间,复制已经在容器中的8个元素,将第9个元素复制到新空间,然后销毁旧元素。如果您使用的是C ++ 11,并且您的元素支持移动构造,则8个副本可以变为8个移动。或者如果你想承担管理指针的开销,你可以存储指向元素而不是元素的指针(复制指针很便宜,但现在你需要处理生命周期问题)。
至于哪个容器比矢量快,这是一个很大的开放式问题。它取决于各种访问/操作模式。 std :: list被提到作为候选人。 O(1)添加到列表的末尾,但可能比向量使用的摊销的O(1)更大的O(1)。你失去了对容器的随机访问权。
答案 5 :(得分:-1)
是push_back()
已知大部分时间都是常量,但需要在size()
到达capacity()
时重新分配整个向量。
如果您想要固定时间插入,则必须尝试std::list
或std::deque
。特别是std::deque
提供了正确的性能,可以在容器末端插入,同时接近其界面中的向量。
因此,它们提供了与向量类似的功能,但是在序列的开头也有效地插入和删除了元素,而不仅仅是在它的结尾。但是,与矢量不同,deques不能保证将所有元素存储在连续的存储位置,因此不允许通过将指针偏移到元素来直接访问。