C++5大构造函数

Posted devbins

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++5大构造函数相关的知识,希望对你有一定的参考价值。

前言

自C++11以来,引入了移动构造函数和移动赋值函数,使得在构造对象的时候可以减少调用次数,以提高性能。

所以C++的构造函数从3个变成了5个,分别是构造函数、拷贝构造函数、拷贝赋值函数、移动构造函数、移动赋值函数。

它们非常相似,放在一起容易搞混,于是总结一下,便有了此文,希望能够对大家有所帮助。

先来看下面的这个例子

#include<iostream>

using namespace std;

class Test{
public:
    Test() {
        cout << "construct" << endl;
    }

    Test(const Test& test) {
        cout << "Copy Construct" << endl;
    }

    Test& operator=(const Test& test) {
        cout << "Copy Assignment Operator" << endl;
        return *this;
    }

    Test(const Test&& test) {
        cout << "Move construct" << endl;
    }

    Test& operator=(Test&& test) {
        cout << "Move Assignment Operator" << endl;
        return *this;
    }
};

Test GenerateTest() {
    return Test();
}

void PassByValue(Test test) {

}

Test&& MoveTest(Test& test) {
    return move(test);
}

int main(int argc, char *argv[])
{
    cout << "----------------1------------------" << endl;
    Test t1;
    cout << "----------------2------------------" << endl;
    Test t2(t1);
    cout << "----------------3------------------" << endl;
    Test t3 = t1;
    cout << "----------------4------------------" << endl;
    Test t4 = Test();
    cout << "----------------5------------------" << endl;
    Test t5 = Test(t1);
    cout << "----------------6------------------" << endl;
    t5 = t2;
    cout << "----------------7------------------" << endl;
    Test t7 = move(t1);
    cout << "----------------8------------------" << endl;
    t7 = move(t2);
    cout << "----------------9------------------" << endl;
    Test t8 = GenerateTest();
    cout << "----------------10------------------" << endl;
    Test t9 = MoveTest(t1);
    cout << "----------------11------------------" << endl;
    Test&& t10 = MoveTest(t1);
    cout << "----------------12------------------" << endl;
    t9 = Test();
    cout << "----------------13------------------" << endl;
    PassByValue(t9);
    return 0;
}

可以猜猜看,会输出什么?

输出

以下结果是开启了编译优化后的输出

----------------1------------------
construct
----------------2------------------
Copy Construct
----------------3------------------
Copy Construct
----------------4------------------
construct
----------------5------------------
Copy Construct
----------------6------------------
Copy Assignment Operator
----------------7------------------
Move construct
----------------8------------------
Move Assignment Operator
----------------9------------------
construct
----------------10------------------
Move construct
----------------11------------------
----------------12------------------
construct
Move Assignment Operator
----------------13------------------
Copy Construct

以下结果是关闭了编译器优化后的输出

在编译的时候加上 -fno-elide-constructors 来关闭优化

----------------1------------------
construct
----------------2------------------
Copy Construct
----------------3------------------
Copy Construct
----------------4------------------
construct
Move construct
----------------5------------------
Copy Construct
Move construct
----------------6------------------
Copy Assignment Operator
----------------7------------------
Move construct
----------------8------------------
Move Assignment Operator
----------------9------------------
construct
Move construct
Move construct
----------------10------------------
Move construct
----------------11------------------
----------------12------------------
construct
Move Assignment Operator
----------------13------------------
Copy Construct

分析

我们把开启优化和关闭优化放一起对比看

序号 开启优化 关闭优化 分析
1 construct construct 调用构造方法,没什么好说的
2 Copy Construct Copy Construct 使用t1初始化t2,创建新对象,调用拷贝构造函数
3 Copy Construct Copy Construct 这里就有点迷惑了,t3是个新对象,所以也是调用拷贝构造函数,而不是拷贝赋值函数
4 construct construct 开启优化后只需要一次构造


Move construct 关闭优化还需要一次移动构造函数,因为 Test() 是个右值
5 Copy Construct Copy Construct 开启优化后只需要一次拷贝构造


Move construct 关闭优化和上一个一样,由于是右值还需要调用一次移动构造
6 Copy Assignment Operator Copy Assignment Operator 由于t5已经存在了,所以调用拷贝赋值函数
7 Move construct Move construct 把t1转换成右值,调用移动构造函数
8 Move Assignment Operator Move Assignment Operator 由于t7已经存在,所以调用移动赋值函数
9 construct construct 开启优化只需要一次构造


Move construct 关闭优化,需要调用两次移动构造,一次是return返回给一个临时对象


Move construct 另一次是把临时对象赋值给t8,临时对象是右值,所以都调用移动构造函数
10 Move construct Move construct 使用 move 把test转为右值,调用移动构造函数
11

只是进行右值绑定,没有创建对象或更新对象,不会调用构造函数
12 construct construct t9是个已经存在的对象,而Test()是个右值,所以先调用构造函

Move Assignment Operator Move Assignment Operator 然后调用移动构造函数
13 Copy Construct Copy Construct 以传值的方式给形参,调用拷贝构造函数

总结

这些构造函数老是把人搞得晕头转向,过几天不看就会忘记搞混,我的记忆方法是抓住两个关键,一个是等号左边的对象,另一个是等号右边的对象。

等号左边的对象决定的是构造还是赋值,也就是如果左边的对象是不存在的就要构造一个新的对象,调用拷贝/移动 构造 。如果对象是存在就是要更新对象的值,也就是要调用拷贝/移动 赋值

等号右边的对象决定的是移动还是拷贝,也就是如果右边的对象是一个右值那么就会调用 移动 构造/赋值。如果右边的对象不是一个右值,那么表示这个对象可能还需要使用,不能移走,所以要调用 拷贝 构造/赋值。

还有一种情况是没有 = 的,这个就很好判断了。无非也就构造和拷贝构造,如果只有一个对象,那就不存在拷贝不拷贝的,肯定是构造,对应上文中的 1 。如果有两个对象,那肯定是拷贝构造,不存在赋值的情况,对应上文中的 2

用一个表格来展示它们之间的关系


对象不存在 对象存在
左值 拷贝构造 拷贝赋值
右值 移动构造 移动赋值

参考资料

[1]

拷贝构造函数和移动构造函数 - 简书: https://www.jianshu.com/p/f5d48a7f5a52


以上是关于C++5大构造函数的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段

Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段

C#VS快捷键

C#VS快捷键

C#VS快捷键

C#VS快捷键