Colin’s Blog

A C++ Programmer

C++ Note 3

本文是我的C++笔记的第三篇 My Thrid C++ Note;

explict

防止类的构造函数的隐式转换

 1class A
 2{
 3private:
 4    int x;
 5public:
 6    A(int x_): x{x_} {}
 7};
 8
 9// ...
10
11A a = 3; // YES
12A a2{3}; // YES

这样是对的

 1class A
 2{
 3private:
 4    int x;
 5public:
 6    explict A(int x_): x{x_} {}
 7};
 8
 9// ...
10
11A a = 3; // NO
12A a{3}; //YES

析构函数 移动构造 和 emplace_back

下列代码

 1class A
 2{
 3private:
 4    int x;
 5
 6public:
 7    A(int xx) : x{xx} {}
 8    A() : A(0) {}
 9    ~A() { cout << "goodbye " << x << endl; }
10};
11
12int main()
13{
14    vector<A> v;
15    v.emplace_back(A{0});
16    v.emplace_back(A{1});
17    v.emplace_back(A{2});
18    cout << "--------------------------------------\n";
19
20    // OUTPUT
21    // goodbye 0
22    // goodbye 0
23    // goodbye 1
24    // goodbye 0
25    // goodbye 1
26    // goodbye 2
27    // --------------------------------------
28    // goodbye 0
29    // goodbye 1
30    // goodbye 2
31}

其原因在于: 首先:emplace_back只有在传入构造参数列表的时候和push_back有区别。其他的时候没有区别。 这里相当于先生成临时对象A{0},再拷贝构造到v中。从而执行了一次对于0的析构 之后,因为要再加入A{1},而目前的v的可用空间大小是1 因此需要再向系统申请一块大小为2的空间。之后再把原来的A{0}复制过去。再放弃原来的大小为1的地址上的A{0},因此又执行一次对于0的析构。之后构造临时对象A1,再复制到v中。则临时对象析构,造成一次对于1的析构。 之后要加入2. 方法一样。先开辟新内存空间,把原来的对象复制过去。再加入2 即可 最终,3个对象的生命周期均到达终点。则依次析构。

1    vector<A> v;
2    v.reserve(10);
3    v.emplace_back(A{0});
4    v.emplace_back(A{1});
5    v.emplace_back(A{2});

在提前预留了空间之后,输出为

1goodbye 0
2goodbye 1
3goodbye 2
4--------------------------------------
5goodbye 0
6goodbye 1
7goodbye 2

我们输出capacity即可看到我们的猜测是正确的。

1    vector<A> v;
2    cout << "CAPACITY " << v.capacity() << endl;
3    v.emplace_back(A{0});
4    cout << "CAPACITY " << v.capacity() << endl;
5    v.emplace_back(A{1});
6    cout << "CAPACITY " << v.capacity() << endl;
7    v.emplace_back(A{2});
8    cout << "CAPACITY " << v.capacity() << endl;
9    cout << "--------------------------------------\n";

得到

 1CAPACITY 0
 2goodbye 0
 3CAPACITY 1
 4goodbye 0
 5goodbye 1
 6CAPACITY 2
 7goodbye 0
 8goodbye 1
 9goodbye 2
10CAPACITY 4
11--------------------------------------
12goodbye 0
13goodbye 1
14goodbye 2

结论: emplace_back以参数列表的形式传入时,不论是否有移动构造函数,都是原地构造,只会调用一次构造函数(只有这一项和push_back有区别,其它都是一样的)

emplace_back以左值对象的形式传入时,不论是否有移动构造函数,都是调用一次拷贝构造函数

emplace_back以右值对象(例如move(左值对象),或者就是右值)的形式传入时 a. 有移动构造函数,调用一次移动构造 b. 没有移动构造函数,调用拷贝构造函数

emplace_back以 Person(“aaa”, “shandong”, 1991) 形式传入时 a. 有移动构造函数,构造临时文件 —> 移动构造 —> 临时文件析构 b. 没有移动构造函数,构造临时文件 —> 拷贝构造 —> 临时文件析构