c++:移动构造函数(转载)

移动构造函数:是c++11新特性,进一步提高程序的效率

说明:

(1):A移动到B,那么A对象就不再使用了

(2):移动:并不是内存中的数据从一个地址到另外一个地址,而是知识所有的变更

拷贝构造函数:Time::Time(const Time &tem, int a = 30)

移动构造函数: Time::Time(const Time &&tem , int a =30)

移动构造函数和移动赋值运算符应该完成的功能:

(1) 完成必要的内存移动,斩断赋值运算符应该完成的功能

(2 ) 确保移动后原对象处于一种“即使被销毁也没有什么问题“的状态

#include <iostream>
using namespace std;

class B
{
public:
	//默认构造函数
	B() :m_bm(100)
	{
		//cout << "调用了B构造函数" << endl;
	}
	//拷贝构造函数
	B(const B & tmp) :m_bm(tmp.m_bm)
	{
		//cout << "这里调用了B拷贝构造函数" << endl;
	}
	//析构函数
	~B()
	{
		//cout << "这里调用了B析构函数" << endl;
	}

	int m_bm;

};
class A
{
public:

	A() :m_pb(new B())
	{
		cout << "类A构造函数" << endl;
	}
	A(const A& tmps) :m_pb(new B( *(tmps.m_pb) )) //调用类B的拷贝构造函数
	{
		cout << "类A 的拷贝构造函数执行了" << endl;
	}	
	//移动构造函数
	//noexcept:通知标准库我们这个移动构造函数不抛出任何异常(提高编译器的工作效率)
	A(A && tmpa)  :m_pb(tmpa.m_pb) {  //原来对象a指向的内存m_pb,直接就让这个临时对象指向这段内存。
		tmpa.m_pb = nullptr;
		cout << "类A的移动构造函数" << endl;
	}

	//拷贝赋值运算符
	A& operator = (const A& src)
	{
		if (this == &src)
			return *this;
		else{
			delete m_pb;  //把自己的内存干掉
			m_pb = new B(*(src.m_pb));  //重新分配一块内存
			std::cout << "类A的拷贝赋值运算符被执行了" << endl;
			return *this;
		}

	}

	//移动赋值运算符
	A& operator = (A && src)
	{
		if (this == &src)
			return *this;
		delete m_pb;//把自己的内存干掉;
		m_pb = src.m_pb;//把对方的内存直接拿过来,直接指过来
		src.m_pb = nullptr; //斩断源(也就是对方和那个内存相关的关联要斩断)
		cout << "类A的移动赋值运算符被执行了" << endl;
		return *this;
	}

	virtual ~A()
	{
		delete m_pb;
		cout << "类A的析构函数被调用了" << endl;
	}

private:
	B *m_pb;
};

static A getA()	      //只能在本.cpp文件使用
{
	A a;
	return a; //临时对象
}

struct TC
{
	int i; //内置类型可以移动
	std::string s;//sting类型定义了自己的移动操作
};

int main()
{
	//(1)
	////移动构造函数演示
	//B *bp = new B();  //new调用类B的构造函数
	//bp->m_bm = 19;
	//B *bp2 = new B(*bp); //这种给参数的new方法会调用B类的拷贝构造函数
	//delete bp;
	//delete bp2;

	////(2)
	//A a = getA();
	////没有移动构造函数的时候:类A:调用了1次构造函数,1次拷贝构造函数,2次析构函数
	////有移动构造函数的时候:调用了了一次构造函数,一次移动构造函数2次析构函数,从而整个程序的效率提高
    
	////(3)
	//A a = getA();	 //1个构造函数,一个移动构造函数,一个析构函数
	//A a2(a);//一个拷贝构造函数
	////(4)
	//A a = getA();  
	//A a2(std::move(a));//建立了新对象,调用新对象a2的移动构造函数
	//A a3 = getA();   
	//A && a4(std::move(a3));  //这里没有建立新的对象,根本不会调用移动构造函数
	//						//效果等同于把对象,有了一个新的别名叫a4,后续建议用a4操作。

	//A &&c = getA(); //从getA()返回的临时对象被c接管了。1个构造函数,一个移动构造函数,一个析构函数



	////移动赋值运算符
	////(4)
	//A a = getA();
	//A a2;
	//a2 = std::move(a);//调用了移动赋值运算符

	////(5)
	////合成的移动操作
	////某些条件下,编译器能合成移动构造函数,移动赋值运算符
	////a)有自己的拷贝构造函数,自己的拷贝赋值运算符,或者自己的析构,那么编译器就不会为它合成移动构造函数和移动赋值运算符
 //   //b)如果我们没有自己的移动构造函数和移动赋值运算符,那么系统调我们自己写的拷贝构造函数和拷贝赋值运算符
	////c)只有一个类没定义自己版本的拷贝构造成员(没有拷贝构造函数和拷贝运算符)。且类的每个非静态成员都可以移动时,编译器才会为该类合成移动构造函数或者移动赋值运算符
	////什么叫做成员可以移动呢?
	////(1)内置类型是可以移动的
	////(2)类类型的成员,则这个类要有对应的移动操作相关的函数,就可以移动。
	////此时编译器就能够为我们合成移动构造函数和移动赋值运算符
	//TC a;
	//a.i = 100;
	//a.s = "I love China!";
	//const char *p = a.s.c_str();
	//
	//TC b = std::move(a); //导致TC类的移动构造函数的执行,string类里的移动构造函数把s移动到b.s,而不是std::move()
	//const char *q = b.s.c_str();
	

	//总结
	//(1) :尽量给类增加移动构造函数和移动赋值运算符
	//(2) :noexcept
	//(3) :该给nullptr 的要给,让被移动对象随时处于一种能够被析构的状态;
	//(4):没有移动会调用拷贝替代。


	system("pause");								 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41543888/article/details/89737248