我想学习push_back()
和emplace_back()
之间的区别,尤其是当元素是类类型时。所以我编写如下代码:
int cnt = 0;
class foo{
public:
//constructor
foo() : ucnt(++cnt) { cout << ucnt << "\tdefault" << endl; }
foo(const foo&) : ucnt(++cnt) { cout << ucnt << "\tcopy" << endl; }
~foo() { cout << ucnt << "\tdestroyed" << endl; }
int ucnt;
};
int main()
{
vector<foo> vf = {foo()};
cout << vf.size() << " : " << vf[0].ucnt << endl;
vf.push_back(foo());
cout << vf.size() << " : " << vf[0].ucnt << " " << vf[1].ucnt << endl;
vf.emplace_back(foo());
cout << vf.size() << " : " << vf[0].ucnt << " " << vf[1].ucnt << " " << vf[2].ucnt << endl;
return 0;
}
其结果是:
1 default
2 copy
1 destroyed
1 : 2
3 default
4 copy
5 copy
2 destroyed
3 destroyed
2 : 5 4
6 default
7 copy
8 copy
9 copy
5 destroyed
4 destroyed
6 destroyed
3 : 8 9 7
8 destroyed
9 destroyed
7 destroyed
似乎
vf
中的所有元素都已复制,然后在执行push_back()
和emplace_back()
时销毁了。为什么? 最佳答案
emplace_back
emplace_back的优点在于,它将其参数直接传递给要植入的类的构造函数,并在原处构造新对象,而不是通过副本构造它。
例如
elections.emplace_back("Nelson Mandela", "South Africa", 1994);
与push_back相反,在其中传递临时对象并进行复制以构造新对象。
例如
elections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936));
所以你的情况应该是
vf.emplace_back();
因为您没有要传递给就位构造函数的ctor参数。相比
vf.push_back(foo());
构造平衡
许多意外的复制和删除源于调整 vector 的大小。
最多1. cout
1个临时创建,并在构造函数中销毁
= 1x默认值,1x副本,1x破坏✔️
最多2个。cout
1个在push_back中创建和销毁的临时文件
将1个现有对象复制到调整大小的 vector 中,从而进行复制创建和销毁
= 1x默认值,2x副本,2x破坏✔️
最多3个。cout
1个在emplace_back中创建和销毁的临时文件
将2个现有对象复制到调整大小的 vector ,从而复制创建并销毁
= 1x默认值,3x副本,3x破坏✔️
等等
编辑:示例
以下代码来自 Artemy Vysotsky ,请参见此答案下的注释,并确切地说明了正确的实现方法。尤其要注意使用
.reserve(3)
以避免重新分配狂欢。#include <vector>
#include <iostream>
using std::cout;
int cnt = 0;
class foo{
public:
//constructor
foo() : ucnt(++cnt) { cout << ucnt << "\tdefault\n" ; }
foo(const foo&) : ucnt(++cnt) { cout << ucnt << "\tcopy\n" ; }
foo(foo&&) noexcept : ucnt(++cnt) { cout << ucnt << "\tmove\n" ; }
~foo() { cout << ucnt << "\tdestroyed\n" ; }
int ucnt;
};
int main()
{
std::vector<foo> vf = {foo()};
cout << vf.size() << " 1: " << vf[0].ucnt << '\n';
vf.reserve(3);
cout << vf.size() << " 2: " << vf[0].ucnt << '\n';
vf.push_back(foo());
cout << vf.size() << " 3: " << vf[0].ucnt << " " << vf[1].ucnt << '\n';
vf.emplace_back();
cout << vf.size() << " 4: " << vf[0].ucnt << " " << vf[1].ucnt << " " << vf[2].ucnt << '\n';
return 0;
}
/***************
Output
$ ./test
1 default
2 copy
1 destroyed
1 1: 2
3 move
2 destroyed
1 2: 3
4 default
5 move
4 destroyed
2 3: 3 5
6 default
3 4: 3 5 6
3 destroyed
5 destroyed
6 destroyed
*****************/