C++的拷贝函数,浅拷贝,深拷贝原理

764 阅读3分钟

1、拷贝函数从结构体的赋值说起

struct Person {
    char * name;
    int age;
};
int main () {
    struct Person p1 = Person{"张三",24};
    struct Person p2 =p1;
    cout << "p1:"<< &p1  << " p2"<<&p2 <<endl;
    return 0 ;
}

在以上代码中,p2的地址和p1的地址是不同的,和我们讲的int赋值是一样的,结构体的赋值也是这样的。 struct Person p2 =p1; 这句话是把 p1 所有的成员复制一份赋给 p2。

2、既然有默认的拷贝函数,我们为什么要实现自定义的拷贝函数?什么是浅拷贝?浅拷贝会引发什么问题?

说这个问题,就要从内存地址说起。搞明白在拷贝构造函数里内存地址的变化,就能明白浅拷贝和深拷贝的原理。下面看一段代码

class Student {
private:
	int age;
	char* name;
public:
	
	//无参构造
	Student() {
		cout << "空参数构造函数" << endl;
	}
	//一个参数的构造方法
	Student(char* name):Student(name,99) {
		cout << "一个参数的构造函数 this" <<this << endl;

	}
	//两个参数的构造函数
	Student(char* name, int age) {
		cout << "两个参数的构造函数 this" << this << endl;
		this->name = (char*)malloc(sizeof(char*) * 10);
		strcpy(this->name, name);
	this->age = age;

	}
	//析构函数
	~Student(){
		cout << "析构函数 this->name" << this->name << endl;
			free(this->name);
		this->name = NULL;
	}
	
	void print() {
		cout << "print this->name:" << static_cast<const void*> (this->name) << " name的值:" << this->name << endl;		
	}
};

int main() {
	char* name = (char *)"好好好";
	Student stu1(name);
	Student stu2 = stu1;//执行拷贝函数
	stu1.print();
	stu2.print();
	return 0;
}

先说结论,运行这段代码会报错,原因是因为在Student stu2 = stu1;的时候执行了拷贝函数,而默认的拷贝函数执行的是浅拷贝,在给stu2这个成员赋值的时候, 由于 char * 是一个指针变量,所以stu2的 name的值,和stu1的name的值是一样的,所以在析构函数里进行free回收的时候,同一个地址会回收两次,导致程序报错。 这就是浅拷贝。浅拷贝只拷贝了数据的地址。 这里需要画一张图来理解。

image.png

所以为了避免这种情况,我们就需要自定义拷贝构造函数。类成员是指针类型变量时,就自己开辟一块空间,把原先的值复制一份放进来。这就是深拷贝。

3、C++中的拷贝构造函数

拷贝函数和空参函数一样,不写都有默认函数,如果自己写了就会覆盖默认的拷贝函数。和Java的空参构造一个道理。
浅拷贝

//拷贝函数
Student::Student(const Student & student) {//常量引用,只读的,不让你改。
    this->name =student->name;
    this->age = student->age -10;//进行减龄操作
}

深拷贝

Student(const Student & student) {
	//深拷贝
	this->name = (char *) malloc(sizeof(char) * 10);
	strcpy(this->name, student.name);

	this->age = student.age;
	cout << "拷贝构造函数 &student " << &student << "this " << this << endl;
}	

image.png

我们再拷贝指针变量类型的数据时,为新构建的对象的成员变量,开辟一块新的空间,再把旧对象的值,复制一份到新空间,这样,我们在回收的时候,就不会冲突了,自己的空间,就可以自己玩了,你再释放就和旧对象没有半毛钱关系了。

4、总结

在C++中,我们书写的成员有指针变量时,比如 char* 和Student * 在传参或者赋值时,构造函数只拷贝了其地址,没有拷贝其内容的情况,我们称之为浅拷贝。会引发同一地址多次释放的错误。我们要真正拷贝内容时,必须需要一个新空间,新地址。来放拷贝过来的内容。这种情况称之为深拷贝。