我想学习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
 *****************/

07-24 14:05