C++ std::vector 创建对象然后添加对象与在向量中创建对象之间的区别?

C++ std::vector difference between creating object then adding it vs creating it inside the vector?

本文关键字:创建对象 向量 之间 区别 对象 vector std 然后 添加 C++      更新时间:2023-10-16

既然std::vector::p ush_back(obj)创建了对象的副本,那么在push_back()调用中创建它是否比以前更有效?

struct foo {
int val;
std::string str;
foo(int _val, std::string _str) :
val(_val), str(_str) {}
};

int main() {
std::vector<foo> list;
std::string str("hi");
int val = 2;
list.push_back(foo(val,str));
return 0;
}
// or
int main() {
std::vector<foo> list;
std::string str("hi");
int val = 2;
foo f(val,str);
list.push_back(f);
return 0;
}
list.push_back(foo(val,str));

请求构造一个foo对象,然后传递到向量中。因此,这两种方法在这方面是相似的。

但是,使用这种方法,c++11 编译器会将foo对象视为"临时"值 (rvalue),并使用void vector::push_back(T&&)函数而不是void vector::push_back(const T&)函数,这在大多数情况下确实更快。您还可以使用以前声明的对象获得此行为:

foo f(val,str);
list.push_back(std::move(f));

另外,请注意(在 c++11 中)您可以直接执行以下操作:

list.emplace_back(val, str);

它实际上有些复杂。对于初学者,我们应该注意std::vector::push_back在两种引用类型上过载:

void push_back( const T& value );
void push_back( T&& value ); 

当我们向push_back传递 lvalue 时,会调用第一个重载,因为只有 lvalue 引用类型可以绑定到左值,就像第二个版本中的f一样。以同样的方式,只有右值引用可以绑定到右值,就像在第一个版本中一样。

它有什么不同吗?仅当类型受益于移动语义时。您没有提供任何复制或移动操作,因此编译器将为您隐式定义它们。他们将分别复制/移动每个成员。因为std::string(您有一个成员)实际上确实受益于字符串很长的移动,所以如果您选择不创建命名对象而是传递右值,您可能会看到更好的性能。

但是,如果您的类型没有从移动语义中受益,则不会看到任何区别。所以总的来说,可以肯定地说,你什么都不会失去,并且可以通过"在调用中创建对象"来获得很多。

说了这么多,我们不能忘记一个向量支持另一种插入方法。你可以通过调用std::vector::emplace_backfoo构造函数的参数直接转发到向量中。这将避免任何中间foo对象,甚至是调用push_back中的临时对象,并将直接在向量打算为其提供的存储中创建目标foo。因此,emplace_back通常可能是最佳选择。

你最好使用

emplace_back(foo(val,str))

如果您要创建新元素并将其推送到向量。因此,您可以执行就地构造。

如果您已经创建了对象,并且确定永远不会将其单独用于其他指令,那么您可以

push_back(std::move(f))

在这种情况下,您的 f 对象是悬空的,他的内容归您的向量所有。