• 【深入理解C++】移动构造函数和移动赋值运算符


    C++11 引入了对象移动而非拷贝的概念,移动并不是把内存中的数据从一个地址挪到另外一个地址,仅仅是所有者的变更。有时候对象发生拷贝后就被销毁了,这种情况下移动而非拷贝对象会大幅度提升性能。

    移动构造函数和移动赋值运算符应该完成的功能:完成必要的内存移动,斩断原对象和内存的关系,确保移动后原对象处于一种“即使被销毁也没有什么问题”的状态,确保移动后不再使用原对象。

    1.拷贝构造函数

    #include 
    using namespace std;
    
    class B
    {
    private:
    	int m_data;
    
    public:
    	B(int x = 0) : m_data(x) {}
    	B(const B& tmpb) : m_data(tmpb.m_data) {}
    	virtual ~B() {}
    };
    
    class A
    {
    private:
    	B* m_pb;
    
    public:
    	A() : m_pb(new B()) // 这里会调用类B的构造函数
    	{
    		cout << "类A的构造函数执行了 - " << this << endl;
    	}
    
    	A(const A& tmpa) : m_pb(new B(*(tmpa.m_pb))) // 这里会调用类B的拷贝构造函数
    	{
    		cout << "类A的拷贝构造函数执行了 - " << this << endl;
    	}
    
    	virtual ~A()
    	{
    		delete m_pb;
    		cout << "类A的析构函数执行了 - " << this << endl;
    	}
    };
    
    static A fun()
    {
    	A a;
    	return a;
    }
    
    int main()
    {
    	fun();
    	cout << "--------------------------------" << endl;
    
    	A a1 = fun();
    	cout << "--------------------------------" << endl;
    
    	A a2(a1);
    	cout << "--------------------------------" << endl;
    
    	A a3(std::move(a1));
    	cout << "--------------------------------" << endl;
    
    	return 0;
    }
    
    • 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
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    输出结果如下:

    在这里插入图片描述

    2.移动构造函数

    #include 
    using namespace std;
    
    class B
    {
    private:
    	int m_data;
    
    public:
    	B(int x = 0) : m_data(x) {}
    	B(const B& tmpb) : m_data(tmpb.m_data) {}
    	virtual ~B() {}
    };
    
    class A
    {
    private:
    	B* m_pb;
    
    public:
    	A() : m_pb(new B()) // 这里会调用类B的构造函数
    	{
    		cout << "类A的构造函数执行了 - " << this << endl;
    	}
    
    	A(const A& tmpa) : m_pb(new B(*(tmpa.m_pb))) // 这里会调用类B的拷贝构造函数
    	{
    		cout << "类A的拷贝构造函数执行了 - " << this << endl;
    	}
    
    	// noexcept:通知标准库这个移动构造函数不抛出任何异常,提高编译器工作效率
    	A(A&& tmpa) noexcept : m_pb(tmpa.m_pb)
    	{
    		tmpa.m_pb = nullptr;
    		cout << "类A的移动构造函数执行了 - " << this << endl;
    	}
    
    	virtual ~A()
    	{
    		delete m_pb;
    		cout << "类A的析构函数执行了 - " << this << endl;
    	}
    };
    
    static A fun()
    {
    	A a;
    	return a; // 如果类A有移动构造函数,那么会调用移动构造函数把a对象的数据移动给临时对象
    }
    
    int main()
    {
    	fun();
    	cout << "--------------------------------" << endl;
    
    	A a1 = fun();
    	cout << "--------------------------------" << endl;
    
    	A a2(a1);
    	cout << "--------------------------------" << endl;
    
    	A a3(std::move(a1));
    	cout << "--------------------------------" << endl;
    
    	return 0;
    }
    
    • 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
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    输出结果如下:

    在这里插入图片描述

    3.拷贝赋值运算符

    #include 
    using namespace std;
    
    class B
    {
    private:
    	int m_data;
    
    public:
    	B(int x = 0) : m_data(x) {}
    	B(const B& tmpb) : m_data(tmpb.m_data) {}
    	virtual ~B() {}
    };
    
    class A
    {
    private:
    	B* m_pb;
    
    public:
    	A() : m_pb(new B()) // 这里会调用类B的构造函数
    	{
    		cout << "类A的构造函数执行了 - " << this << endl;
    	}
    
    	A(const A& tmpa) : m_pb(new B(*(tmpa.m_pb))) // 这里会调用类B的拷贝构造函数
    	{
    		cout << "类A的拷贝构造函数执行了 - " << this << endl;
    	}
    
    	// noexcept:通知标准库这个移动构造函数不抛出任何异常,提高编译器工作效率
    	A(A&& tmpa) noexcept : m_pb(tmpa.m_pb)
    	{
    		tmpa.m_pb = nullptr;
    		cout << "类A的移动构造函数执行了 - " << this << endl;
    	}
    
    	A& operator=(const A& src)
    	{
    		if (this == &src) return *this;
    		delete m_pb;
    		m_pb = new B(*(src.m_pb));
    		cout << "类A的拷贝赋值运算符执行了 - " << this << endl;
    		return *this;
    	}
    
    	virtual ~A()
    	{
    		delete m_pb;
    		cout << "类A的析构函数执行了 - " << this << endl;
    	}
    };
    
    static A fun()
    {
    	A a;
    	return a;
    }
    
    int main()
    {
    	A a1;
    	cout << "--------------------------------" << endl;
    
    	a1 = fun();
    	cout << "--------------------------------" << endl;
    
    	A a2;
    	cout << "--------------------------------" << endl;
    
    	a2 = std::move(a1);
    	cout << "--------------------------------" << endl;
    
    	return 0;
    }
    
    • 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
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    输出结果如下:

    在这里插入图片描述

    4.移动赋值运算符

    #include 
    using namespace std;
    
    class B
    {
    private:
    	int m_data;
    
    public:
    	B(int x = 0) : m_data(x) {}
    	B(const B& tmpb) : m_data(tmpb.m_data) {}
    	virtual ~B() {}
    };
    
    class A
    {
    private:
    	B* m_pb;
    
    public:
    	A() : m_pb(new B()) // 这里会调用类B的构造函数
    	{
    		cout << "类A的构造函数执行了 - " << this << endl;
    	}
    
    	A(const A& tmpa) : m_pb(new B(*(tmpa.m_pb))) // 这里会调用类B的拷贝构造函数
    	{
    		cout << "类A的拷贝构造函数执行了 - " << this << endl;
    	}
    
    	// noexcept:通知标准库这个移动构造函数不抛出任何异常,提高编译器工作效率
    	A(A&& tmpa) noexcept : m_pb(tmpa.m_pb)
    	{
    		tmpa.m_pb = nullptr;
    		cout << "类A的移动构造函数执行了 - " << this << endl;
    	}
    
    	A& operator=(const A& src)
    	{
    		if (this == &src) return *this;
    		delete m_pb;
    		m_pb = new B(*(src.m_pb));
    		cout << "类A的拷贝赋值运算符执行了 - " << this << endl;
    		return *this;
    	}
    
    	A& operator=(A&& src) noexcept
    	{
    		if (this == &src) return *this;
    		delete m_pb;
    		m_pb = src.m_pb;
    		src.m_pb = nullptr;
    		cout << "类A的移动赋值运算符执行了 - " << this << endl;
    		return *this;
    	}
    
    	virtual ~A()
    	{
    		delete m_pb;
    		cout << "类A的析构函数执行了 - " << this << endl;
    	}
    };
    
    static A fun()
    {
    	A a;
    	return a;
    }
    
    int main()
    {
    	A a1;
    	cout << "--------------------------------" << endl;
    
    	a1 = fun();
    	cout << "--------------------------------" << endl;
    
    	A a2;
    	cout << "--------------------------------" << endl;
    
    	a2 = std::move(a1);
    	cout << "--------------------------------" << endl;
    
    	return 0;
    }
    
    • 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
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    输出结果如下:

    在这里插入图片描述

    5.合成的移动构造函数和移动赋值运算符

    如果一个类定义了自己的拷贝构造函数、拷贝赋值运算符或析构函数,编译器就不会为它合成移动构造函数和移动赋值运算符。

    如果我们没有自己的移动构造函数和移动赋值运算符,那么系统会调用我们自己写的拷贝构造函数和拷贝赋值运算符来代替。

    当一个类没有定义任何自己版本的拷贝构造函数、拷贝赋值运算符、析构函数,且类的每个非静态数据成员都可以移动时,编译器才会为它合成移动构造函数或移动赋值运算符。

    • 对于内置类型,是可以移动的。
    • 对于类类型,只要这个类有对应的移动操作的相关函数,就可以移动。
  • 相关阅读:
    Python获取命令行参数的方法sys.argv
    springboot+jsp汽车配件管理系统idea maven 项目lw
    CAN FD canfd适配器USBCANFD的功能简介
    LINUX安装nginx
    c++ 广度优先搜索(Breadth-First Search,BFS)
    C++ 重载运算符和重载函数
    FME大规模转换OSM PBF数据
    Python与数据分析--Matplotlib-2
    快速安装k8s
    在windows上配置本地域名解析,配置hosts文件
  • 原文地址:https://blog.csdn.net/qq_42815188/article/details/101268235