注:编码工具是CLion+Cygwin64
目录
拷贝构造函数
C++在给一个对象引用赋值、函数返回对象或者对象作为函数参数传递的时候,会调用类的拷贝构造函数创建一个新的对象。
赋值
#include <iostream>
using namespace std;
class Student {
public:
char *name;
Student(char *name) : name(name) {
cout << "构造函数" << endl;
}
Student(const Student &student) {
cout << "自定义拷贝构造函数" << endl;
this->name = student.name;
}
};
int main() {
Student stu("Yes");
Student copy = stu;
cout << "stu.name = " << stu.name << ", copy.name = "
<< copy.name << endl;
printf("stu的地址是%p, copy的地址是%p\n", &stu, ©);
return 0;
}
输出:
构造函数
自定义拷贝构造函数
stu.name = Yes, copy.name = Yes
stu的地址是0xffffcc38, copy的地址是0xffffcc30
函数返回对象
在CLion上演示不出来效果,不调用拷贝构造函数,在Visual Studio上可以。
对象作为函数参数传递
#include <iostream>
#include <cstring>
using namespace std;
class Student{
public:
char * name;
Student(char * name):name(name){
printf("调用了构造函数\n\n");
}
Student(const Student & student){
printf("调用了拷贝构造函数:\n");
this->name = student.name;
printf("this的地址是%p\nstudent的地址是%p\n", this, &student);
printf("this->name的地址是%p\nstudent.name的地址是%p\n\n",
this->name, student.name);
}
};
void showStudent(Student student){
cout << "showStudent函数中student.name = " << student.name << endl;
printf("showStudent函数中student.name的地址是%p\n", student.name);
}
int main(){
Student student("Catch");
showStudent(student);
cout << "main函数中student.name = " << student.name << endl;
printf("main函数中student.name的地址是%p\n", student.name);
return 0;
}
输出:
调用了构造函数
调用了拷贝构造函数:
this的地址是0xffffcc38
student的地址是0xffffcc30
this->name的地址是0x1004030f8
student.name的地址是0x1004030f8
showStudent函数中student.name = Catch
showStudent函数中student.name的地址是0x1004030f8
main函数中student.name = Catch
main函数中student.name的地址是0x1004030f8
从输出内容中的地址可以看出来,main函数中和showStudent函数中的student不是一个对象,因为它们的内存地址不同。但注意看,两个student对象的成员属性name的内存地址是相同的。
浅拷贝
类的默认拷贝构造函数和自定义的不使用动态开辟内存方式创建变量的拷贝构造函数的成员拷贝方式都是浅拷贝。上面说的几种情况都是浅拷贝。
浅拷贝会引发一个问题:重复释放相同地址内存。当被拷贝的对象的成员属性是动态开辟内存方式创建的,在析构函数会进行释放,但是浅拷贝方式实现的拷贝构造函数会让对象成员属性直接保存被拷贝对象的成员属性所保存的内存地址,这样就造成了,当两个对象有一个被释放,该成员属性就会因调用析构函数而被释放,而另一个对象再被释放调用析构函数时,因为该属性已经被释放过了,就会重复释放,接着就报错了。
错误示例:
#include <iostream>
#include <cstring>
using namespace std;
class Student{
public:
char * name;
Student(char * name){
cout << "构造函数被调用" << endl;
this->name = (char*)malloc(sizeof(char) * 10);
strcpy(this->name, name);
}
Student(const Student & stu){
cout << "拷贝构造函数被调用" << endl;
this->name = stu.name;
}
~Student(){
cout << "析构函数被调用" << endl;
free(name);
name = NULL;
}
};
int main(){
Student stu("Telepathy");
Student copy = stu;
return 0;
}
深拷贝
为拷贝对象的成员属性用动态开辟内存方式创建内存,解决因浅拷贝造成的重复释放相同内存问题。
#include <iostream>
#include <cstring>
using namespace std;
class Student{
public:
char * name;
Student(char * name){
cout << "构造函数被调用" << endl;
this->name = (char*)malloc(sizeof(char) * 10);
strcpy(this->name, name);
}
Student(const Student & stu){
cout << "拷贝构造函数被调用" << endl;
// this->name = stu.name;
// 深拷贝
this->name = (char*) malloc(sizeof(char) * 10);
strcpy(this->name, stu.name);
}
~Student(){
cout << "析构函数被调用" << endl;
free(name);
name = NULL;
}
};
int main(){
Student stu("Telepathy");
Student copy = stu;
return 0;
}
与浅拷贝的错误示例的不同之处在拷贝构造函数中,用动态开辟内存的方式替代了直接赋值的方式。