从push_back
调用方法std::vector
时,其大小增加1,意味着创建新实例,然后您传递的参数将被复制到最近创建的元素中,对?例如:
myVector.push_back(MyVectorElement());
那么,如果我想简单地使用其默认值来增加向量的大小,那么使用resize
方法不是更好吗?我的意思是这样的:
myVector.resize(myVector.size() + 1);
据我所知,这将完成同样的事情,但会避免完全不必要的元素属性的赋值副本。
这种推理是正确的还是我错过了什么?
答案 0 :(得分:19)
至少对于GCC来说,使用哪个并不重要(结果如下)。但是,如果你不得不担心它,你应该使用指针或(甚至更好)某种形式的smart pointers.。我当然会推荐the ones in the boost library。
如果您想知道在实践中哪个更好用,我建议使用push_back
或reserve
,因为调整大小会在每次调用时调整向量的大小,除非它的大小与要求的尺寸。 push_back
和reserve将仅在需要时调整向量的大小。这是一件好事,好像你想要将矢量调整到size+1
,它可能已经在size+20
,因此调用调整大小不会带来任何好处。
测试代码
#include <iostream>
#include <vector>
class Elem{
public:
Elem(){
std::cout << "Construct\n";
}
Elem(const Elem& e){
std::cout << "Copy\n";
}
~Elem(){
std::cout << "Destruct\n";
}
};
int main(int argc, char* argv[]){
{
std::cout << "1\n";
std::vector<Elem> v;
v.push_back(Elem());
}
{
std::cout << "\n2\n";
std::vector<Elem> v;
v.resize(v.size()+1);
}
}
测试输出
1
Construct
Copy
Destruct
Destruct
2
Construct
Copy
Destruct
Destruct
答案 1 :(得分:16)
我发现myVector.push_back(MyVectorElement());
更直接,更容易阅读。
问题是,resize
不仅仅调整了那些地方的数组和默认构造元素的大小;这就是它默认的内容。它实际上需要第二个参数,即每个新元素的副本,默认为T()
。从本质上讲,您的两个代码示例完全相同。
答案 2 :(得分:6)
在EA(电子艺界),这被认为是一个很大的问题,他们编写了自己的STL版本,EASTL,其中许多其他内容包括push_back(void)
vector
} class。
答案 3 :(得分:6)
关于Yacobi接受的答案的测试代码的c ++ 0x观点:
答案 4 :(得分:4)
当你执行push_back()时,该方法会检查底层存储区域以查看是否需要空间。如果需要空间,那么它将为所有元素分配一个新的连续区域,并将数据复制到新区域。
但是:新分配的空间的大小不仅仅是一个更大的元素。它使用了一个漂亮的小算法来增加空间(我不认为算法被定义为标准的一部分,但它通常会使分配的空间加倍)。因此,如果您推送大量元素,则只有一小部分元素实际上会导致重新分配基础空间。
要手动实际增加分配空间,您有两个选择:
reserve()
这增加了底层存储空间,而没有向向量添加元素。因此,未来push_back()
调用将不太可能需要增加空间。
resize()
这实际上会向向量添加/删除元素以使其大小正确。
capacity()
是否需要重新分配底层存储之前可以存储的元素总数。因此,如果capacity() > size()
push_back不会导致重新分配矢量存储。
答案 5 :(得分:3)
你是对的,push_back
无法避免至少一个副本,但我认为你担心错误的事情,但resize
也不一定会表现得更好(它复制了它的价值)默认为默认构造临时的第二个参数。)
vector
不适合复制昂贵的对象。 (几乎)任何push_back
或resize
都可能导致vector
的每个当前成员以及任何新成员被复制。
答案 6 :(得分:2)
myVector.resize(myVector.size() + 1);
将调用MyVectorElement的空构造函数。你想达到什么目的?为了在向量中保留空间(并节省内存分配开销),有reserve()方法。你无法避免构造函数。
答案 7 :(得分:2)
当您调用push_back
时,假设不需要调整基础存储的大小,vector
类将使用“placement new”运算符来就地复制构造新元素。在复制构造之前,向量中的元素将不默认构造。
当您致电resize
时,几乎会发生完全相同的序列。 vector
分配存储空间,然后通过将新位置复制到每个新位置来复制默认值。
结构如下:
::new (p) _T1(_Val);
其中p
是指向向量存储的指针,_T1
是存储在向量中的类型,_Val
是“默认值”参数(默认为{{ 1}})。
简而言之,resize和push_back在幕后做同样的事情,速度差异可能是由于多个内部分配,多个数组边界检查和函数调用开销。时间和内存的复杂性是一样的。
答案 8 :(得分:2)
显然你担心效率和性能。
std :: vector实际上是一个非常好的表演者。如果您大致知道它可能会有多大,请使用reserve方法预分配空间。显然,这是以可能浪费的内存为代价的,但如果你经常使用push_back,它会对性能产生很大的影响。
我认为它的实现取决于向量预先保留多少内存(如果有的话),或者在添加元素时保留多少以供将来使用。最糟糕的情况是你所说的 - 一次只能增加一个元素。
在您的应用中尝试进行一些性能测试,无需保留和使用它进行比较。
答案 9 :(得分:1)
push_back :您创建对象并将其复制到向量中 调整大小:向量使用默认构造函数创建对象并将其复制到向量中。
速度差异:您必须测试STL和编译器的实现,但我认为这无关紧要。
答案 10 :(得分:0)
我怀疑实际答案是STL实现和编译器使用的强大功能,但是,“resize”函数具有原型(ref)
void resize( size_type num, TYPE val = TYPE() );
表示val是默认构造的,并通过placement new和copy-constructor复制到新分配的(或可能以前分配但未使用的)空间中。因此,这两个操作都需要相同的操作序列:
最好是推迟更清晰,更通用(就STL容器而言)push_back,而不是应用过早优化 - 如果探查器突出显示push_back作为热点,那么最可能的原因是内存分配,即最好通过明智地使用储备来解决。