拷贝构造函数
-
拷贝构造函数也是构造函数,长相和构造函数一样的,只是参数是固定
-
拷贝构造函数唯一的参数是对对象引用
-
-
不写拷贝构造函数,也存在一个默认的拷贝构造函数
-
拷贝构造函数作用: 通过一个对象去初始化另一个对象
-
思考问题
-
什么时候调用拷贝构造?
-
当通过一个对象去创建出来另一个新的对象时候需要调用拷贝
-
-
拷贝构造什么时候需要加const修饰参数?
-
当存在匿名对象赋值操作的时候,必须要const修饰
-
-
-
class MM { public: MM() = default; MM(string name, int age) :name(name), age(age) {} void print() { cout << name << "\t" << age << endl; } //拷贝构造 MM(const MM& mm) //MM girl(mm); { name = mm.name; //girl.name=mm.name age = mm.age; //girl.age=mm.age cout << "拷贝构造" << endl; } protected: string name; int age; }; int main() { MM mm("mm", 18); mm.print(); //显示调用调用 cout << "显示调用" << endl; MM girl(mm); //通过一个对象创建另一个对象 girl.print(); //隐式调用 cout << "隐式调用" << endl; MM girl2 = mm; //拷贝构造 girl2.print(); /* 打印结果 mm 18 显示调用调用 拷贝构造 mm 18 隐式调用 拷贝构造 mm 18*/ return 0; }
- 上述的代码可以看到,通过一个对象创建另一个对象的方式有小括号赋值和等号运算符。
- 我们在还可以通过函数传参的方式来进行对象的拷贝。
-
class MM { public: MM() = default; MM(string name, int age) :name(name), age(age) {} void print() { cout << name << "\t" << age << endl; } //拷贝构造 MM(const MM& mm) { name = mm.name; age = mm.age; cout << "拷贝构造" << endl; } protected: string name; int age; }; void printData1(MM mm1) /*MM mm=实参;这是又定义了一个类类型的mm1, 再将main函数中的mm拷贝给mm1,相当于MM mm1(mm)*/ { mm1.print(); } void printData2(MM& mm1) /*不存在拷贝本 这是给main函数中的mm起别名, 所以该函数的打印与直接打印main函数中的mm是一样的*/ { mm1.print(); } int main() { MM mm = { "小红",20 }; //函数传参 cout << "第一种调用形态" << endl; printData1(mm); cout << "第二种调用形态" << endl; printData2(mm); //由打印结果可以看出,运用函数传参的时候引用与不引用的区别 /* 打印结果 第一种调用形态 拷贝构造 小红 20 第二种调用形态 小红 20*/ return 0; }
- 还有一种对象——无名对象(匿名对象)
- 匿名对象就是没有定义名字的对象,并且使用匿名对象的时候,拷贝函数的参数必须有const修饰
//无名对象 匿名对象 MM temp; temp = MM("匿名", 18); temp.print(); //匿名对象创建对象时候,拷贝构造一定要用const修饰 MM temp2 = MM("匿名", 199); temp2.print(); /* 打印结果 匿名 18 匿名 199*/
如果不加入const修饰:
深浅拷贝
浅拷贝: 默认的拷贝构造叫做浅拷贝
深拷贝: 拷贝构造函数中做了new内存操作,并且做拷贝赋值的操作
-
如果我们在使用了指针,并且new了一段内存,使用默认的拷贝函数时,就会发生问题
- 这里可能会有一点搞不清楚为什么发生程序崩溃的错误,我们来画图描述一下。
- 这样的拷贝,只是改变了mm1的name的指针方向,它与mm共同指向同一个内存,然后我们又知道,指针在最后是需要析构函数来释放的,那么mm的name指针指向的内容被释放了,mm1的name指针又该指向什么呢?
- 所以这就是为什么在使用指针的时候,我们已经不能使用默认的拷贝函数,需要直接再写一个拷贝函数——也就是深拷贝
class MM { public: MM(const char* mname, int age) :age(age) { name = new char[strlen(mname) + 1]; strcpy_s(name, strlen(mname) + 1, mname); } MM(const MM& object) //这就是深拷贝函数(一定是引用) { //name = object.name; name = new char[strlen(object.name) + 1]; //这里重新给需要拷贝的name申请了一段新的空间 strcpy_s(name, strlen(object.name) + 1, object.name); //name = object.name; age = object.age; } void print() { cout << name << "\t" << age << endl; } ~MM() { delete[] name; name = nullptr; } protected: char* name; int age; }; int main() { { MM mm = { " 小红",20 }; MM mm1 = mm; mm.print(); mm1.print(); } /* 打印结果 小红 20 小红 20*/ return 0; }
-
使用深拷贝函数的时候,就可以重新给一段新的内存,这样两个对象的name指针都会指向不同的两个空间,但是他们的的内容是一样的,那么他们进行析构函数释放内存的时候就不会发生冲突。