emplace_back( )

在容器末尾添加一个新元素,它通常使用 placement-new 在容器提供的位置就地构造元素。

通常要保证操作后的size不大于旧的capacity(end迭代器要重新生成),否则需要重新分配内存,这种情况下所有迭代器和对元素的所有引用都会失效。

下面代码是从cpp reference抄来的,理解一下怎么运行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <vector>
#include <cassert>
#include <iostream>
#include <string>

struct President
{
std::string name;
std::string country;
int year;

President(std::string p_name, std::string p_country, int p_year)
: name(std::move(p_name)), country(std::move(p_country)), year(p_year)
{
std::cout << "I am being constructed.\n";
}

President(President&& other)
: name(std::move(other.name)), country(std::move(other.country)), year(other.year)
{
std::cout << "I am being moved.\n";
}

President& operator=(const President& other) = default;
};

int main()
{
std::vector<President> elections;
std::cout << "emplace_back:\n";
auto& ref = elections.emplace_back("Nelson Mandela", "South Africa", 1994);
assert(ref.year == 1994 && "uses a reference to the created object (C++17)");

std::vector<President> reElections;
std::cout << "\npush_back:\n";
reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936));

std::cout << "\nContents:\n";
for (President const& president: elections)
std::cout << president.name << " was elected president of "
<< president.country << " in " << president.year << ".\n";

for (President const& president: reElections)
std::cout << president.name << " was re-elected president of "
<< president.country << " in " << president.year << ".\n";
}

下面的是Output:

1
2
3
4
5
6
7
8
9
10
emplace_back:
I am being constructed.

push_back:
I am being constructed.
I am being moved.

Contents:
Nelson Mandela was elected president of South Africa in 1994.
Franklin Delano Roosevelt was re-elected president of the USA in 1936.

解析代码

= default

President& operator=(const President& other) = default;

这串代码里的= default使得编译器将会提供该函数的默认实现,并且保证这些默认实现不会被删除;构造函数和析构函数将提供默认的空实现。

作用:

1、生成默认实现:当一个类没有显式定义构造函数或析构函数时,编译器会自动生成默认的构造函数或析构函数。如果我们在类中使用了default关键字,则可以显式地告诉编译器使用默认的实现。

2、防止默认实现被删除:在某些情况下,编译器不会生成默认的构造函数或析构函数,例如当类包含了一个用户自定义的构造函数或析构函数时。如果我们想要让编译器生成默认的实现,可以在类定义中使用default关键字来显式声明默认的构造函数或析构函数。(例子里属于这种)

std : : move

name(std::move(p_name))这里的p_name作为一个右值,通过std::move绑定左值name,相当于构造函数里给name赋值。

构造函数President

这里构造函数有两种手动实现,第一种直接传入三个参数构造,第二种传入类other再构造,通过输出很容易发现,通过emplace_back构造的函数直接通过第一种构造,传入参数,不需要生成类再拷贝。

通过push_back的输出发现,会先构造President类,再通过第二种构造拷贝构造,需要两步。

c++11的for

for (President const& president: elections)

先定义一个变量,这里是president,然后从序列elections一个一个取出全部元素循环。(例子里这里只有一个President变量elections,只循环一次,将elections作为president取出)。

总结

这段代码就是让你看透emplace_back和push_back添加元素的本质区别。