前提回顾:
我们之前学过了类内部的初始化构造函数有如下几种
构造函数
拷贝构造函数
赋值运算符拷贝构造函数
今天还是学习一个 移动构造函数
我先将前面学习的三种构造函数的写法以及运用场景整理如下:
构造方法,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手动的重写了 移动赋值运算符,就会走 移动赋值运算符函数;如果没有写,会走 =号的函数
、