28 C++ 对象移动,移动构造函数,移动赋值运算符

前提回顾:

我们之前学过了类内部的初始化构造函数有如下几种

构造函数

拷贝构造函数

赋值运算符拷贝构造函数

今天还是学习一个 移动构造函数

我先将前面学习的三种构造函数的写法以及运用场景整理如下:

构造方法,copy构造函数,等号赋值运算符 构造函数
//构造函数,拷贝构造函数,赋值运算符拷贝构造函数 总结整理
class Teacher77 {

public:
	Teacher77() {

	}
	//构造函数
	Teacher77(int age/*in*/,
		char *name/*in*/,
		string address/*in*/,
		char *title/*in*/,
		char ** studentname/*in*/,
		string * familyname):m_age(age),
		m_address(address){
		//char m_name[64]需要copy
		strcpy_s(this->m_name,sizeof(this->m_name),name);
		//title是 char *
		this->m_title = title;
		//studentname 是char **
		this->m_studentname = studentname;
		this->m_familyname = familyname;
		cout << "构造函数被调用" << endl;
	}

	// copy 构造函数被调用
	Teacher77  (const Teacher77 & obj)
		:m_age(obj.m_age),
		m_address(obj.m_address),
		m_title(obj.m_title),
		m_studentname(obj.m_studentname),
		m_familyname(obj.m_familyname){

		//char m_name[64]需要copy
		strcpy_s(this->m_name, sizeof(this->m_name), obj.m_name);
		cout << "copy 构造函数被调用" << endl;
		
	}

	// 赋值等号运算符 被调用

	Teacher77& operator=(Teacher77& obj){
		this->m_age = obj.m_age;
		strcpy_s(this->m_name, sizeof(this->m_name), obj.m_name);
		this->m_address = obj.m_address;
		this->m_title = obj.m_title;
		this->m_familyname = obj.m_familyname;
		this->m_studentname = obj.m_studentname;
		cout << "等号赋值运算符构造函数被调用" << endl;
		return *this;
	}

private:
	int m_age; //年龄
	char m_name[64] = { 0 };//名字
	string m_address;//家庭住址
	char *m_title; // 职称,
	char **m_studentname;//学生名字,我们目前 认为一个老师只带了三个学生
	string *m_familyname;//家人信息

public:
	void printTeacher() {
		cout << "  m_age = " << this->m_age << endl;
		cout << "  m_name = " << this->m_name << endl;
		cout << "  m_address = " << this->m_address << endl;
		cout << "  m_title = " << this->m_title << endl;
		int i = 0;
		while (this->m_studentname[i] !=nullptr)
		{
			cout << "  m_studentname[" << i << "] = " << this->m_studentname[i];
			i++;
		}
		cout << endl;

		int j = 0;
		while (!this->m_familyname[j].empty())
		{
			cout << "  m_familyname[" << j << "] = " << this->m_familyname[j];
			j++;
		}
		cout << endl;
	}

	void setTitle(char * newtitle) {
		this->m_title = newtitle;
	}
};

void main() {
	//第一种构造方法,全部元素都是在 栈 中 弄出来
	int age = 28;
	char name[64] = "teachername";
	string address("teacheraddress");
	char *title = (char *)"teachertitle";
	char *studentname[4] = { (char *)"student111" ,(char *)"student222" ,(char *)"student333",nullptr };//最后一个值写成nullptr,是为了在读取的时候,判断最后一位,在C++中,这叫做岗哨
	string familyname[6] = { "baba","mama","son","nver","sunzi","" };//最后一个值写成"",是为了在读取的时候,判断最后一位,在C++中,这叫做岗哨.岗哨不一定是nullptr,也不一定是"",根据实际类型和您的业务逻辑来判断写成什么,只是nullptr ,null,""这些比较常用
	Teacher77 tea(age,name,address,title,studentname, familyname);
	tea.printTeacher();
	cout << "-------" << endl;
	//第一种 copy 构造方法
	Teacher77 tea2 = tea;
	tea2.printTeacher();
	cout << "-------" << endl;
	tea2.setTitle((char *)"newtitle");
	tea2.printTeacher();
	cout << "-------" << endl;
	tea.printTeacher();
	cout << "-------" << endl;

	//第一种 等号运算符重载 构造方法被调用
	Teacher77 tea3;
	tea3 = tea2;
	tea3.printTeacher();
}

移动构造函数 学习

C++ 11中引入。

使用场景

将对象a 中的数据 转让给 对象b,这部分数据主要是指针。

那么对象a中的数据就不能再次使用了。

需要考虑到a在析构时的问题,如果已经将a给b 了,那么a中的指针如果和b同时 delete,就会有问题。

因此在 移动构造函数中让切断a对象的指针指向,也就是让对象a中的指针要指向nullpotr

如可能,请尽量的给类中添加 移动构造 和移动赋值

如果没有手动的写移动构造函数,那么C++编译器会调用 拷贝函数代替。

移动构造函数 最好后面加上 noexcept,这样的含义是:告知C++ 我不会抛出异常

移动构造函数的写法:


//移动构造函数


class Teacher79 {
public:
	Teacher79() {
		cout << "Teacher79的 构造函数 被调用" << endl;
	}

	~Teacher79() {
		cout << "Teacher79的 析构函数 被调用" << endl;
	}

	Teacher79(const Teacher79 &obj) {
		cout << "Teacher79的 copy 构造函数被调用" << endl;
	}

	Teacher79& operator= (const Teacher79& obj) {
		cout << "Teacher79的 = 运算符 被调用" << endl;
		return *this;
	}

	Teacher79(Teacher79 &&obj) {
		cout << "Teacher79的 移动构造函数  被调用" << endl;
		
	}
};

class Teacher80 {
public:
	Teacher80() :pt79(new Teacher79()) {
		cout << "Teacher80的构造函数被调用" << endl;
	}

	~Teacher80() {
		if (pt79 != nullptr) {
			delete pt79;
			pt79 = nullptr;
			cout << "delete pt79" << endl;
		}
		cout << "Teacher80的析构函数被调用" << endl;
	}

	Teacher80(const Teacher80 &obj):pt79(new Teacher79(*(obj.pt79))) {
		cout << "Teacher80的 copy 构造函数被调用" << endl;
	}

	Teacher80(Teacher80 &&obj) noexcept {
		cout << "Teacher80的 移动构造函数  被调用" << endl;
		this->pt79 = obj.pt79;
		obj.pt79 = nullptr;

	}


	Teacher80& operator= (const Teacher80& obj) {
		cout << "Teacher80的 = 运算符 被调用" << endl;
		return *this;
	}



private:
	Teacher79 *pt79;

public:

};


static Teacher80 getTeacher80() {
	Teacher80 tempt80;
	return tempt80;

	//Teacher79的 构造函数 被调用  tempt80的构造
	//	Teacher80的构造函数被调用  tempt80的构造
	//	Teacher80的 移动构造函数  将tempt80 通过移动构造传递给了一个看不见的临时对象,
	//	Teacher80的析构函数被调用  tempt80的析构 由于tempt80指向了移动构造函数,pt79已经被置为nullptr了,因此这里tempt80的析构,没有打印析构pt79
	//	Teacher79的 析构函数 被调用 //由于临时对象并没有人接,因此立即也执行了析构函数,临时对象是有pt79的,因此会打印析构pt79的log
	//	delete pt79
	//	Teacher80的析构函数被调用
}


void main() {
	getTeacher80();
	cout << "duandian2" << endl;
}

对比 copy 构造函数和 移动构造函数

	//copy 构造函数
    Teacher80(const Teacher80 &obj):pt79(new Teacher79(*(obj.pt79))) {
		cout << "Teacher80的 copy 构造函数被调用" << endl;
	}

//copy 构造函数 和 上面那个一样,因此只能写一个,这里写两个是练手,上一个写法包含了初始化函数参数列表的行为。
	Teacher80(const Teacher80 &obj) {
		cout << "Teacher80的 copy 构造函数被调用" << endl;
		this->pt79 = new Teacher79(*(obj.pt79));
	}

//移动 构造函数

	Teacher80(Teacher80 &&obj) noexcept {
		cout << "Teacher80的 移动构造函数  被调用" << endl;
		this->pt79 = obj.pt79;
		obj.pt79 = nullptr;

	}

移动赋值运算符的写法

	Teacher80& operator= ( Teacher80&& obj) {
		cout << "Teacher80的 移动赋值 运算符 被调用" << endl;
		if (this == &obj) {
			return *this;
		}
		//先把自己的内存干掉
		delete this->pt79;
		this->pt79 = obj.pt79;//再将obj 的pt79 拿过来用
		obj.pt79 = nullptr; // 最后斩断obj的指针指向

		return *this;
	}

调用
	Teacher80 t1;
	Teacher80 t2;
	t2 = move(t1);//由于 Teacher80手动的重写了 移动赋值运算符,就会走 移动赋值运算符函数;如果没有写,会走 =号的函数
	

你可能感兴趣的:(c++)