深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝操作(比如拷贝构造函数),两个数据指向堆区中同一个内存
深拷贝:在堆区重新申请空间,进行拷贝操作,两个数据指向堆区中不同的内存
我们来看看以下代码运行后有什么问题
#include<iostream>
using namespace std;
#include<string>
//深浅拷贝是面试经典问题,也是常见的一个坑
//浅拷贝:简单的赋值拷贝操作
//深拷贝:在堆区重新申请空间,进行拷贝操作
class Person {
public:
int age;
int* height; //身高,因为要在堆区开辟数据,所以设置为指针
Person() {
cout << "Person的默认构造函数被调用" << endl;
}
Person(int a,int b) {
age = a;
height = new int(b);
cout << "Person的有参构造函数被调用" << endl;
}
~Person() {
// 析构代码,将堆区开辟数据做释放操作
if (height != NULL) {
delete height;
height = NULL;
}
cout << "Person的析构函数被调用" << endl;
}
};
void test01() {
Person p1(18,178);
cout << "p1的年龄为" << p1.age <<" "<<"身高为:"<<*p1.height<< endl;
Person p2(p1);// 拷贝构造函数(浅拷贝),把p1的所有属性拷贝给p2,导致p2.height=p1.height,两个指针指向同一块堆区内存
cout << "p2的年龄为" << p2.age << " " << "身高为:" << *p2.height << endl;
}
int main() {
test01();
system("pause");
return 0;
}
注意析构函数大部分都是空函数,只有当在堆区开辟的数据需要释放时才需要添加代码
上述代码,编译通过,但运行时发生崩溃
原因如下:
浅拷贝的问题需要深拷贝来解决,深拷贝构造函数需要自己写
深拷贝代码如下:
// 为了解决浅拷贝的问题,得自己写个拷贝构造函数,并使用深拷贝
Person(const Person& p) {
age = p.age;
// 编译器默认会提供拷贝构造函数: height=p.height;但这是浅拷贝,会引起中断(堆区被重复释放),因为浅拷贝就是简单的复制粘贴,
// 深拷贝
height = new int (*p.height);
//这句话的意思是:在堆区新开辟一块内存,内存里放的数据是*p.height,用this->height指向这片堆区内存
cout << "Person的拷贝构造函数被调用" << endl;
}
深拷贝就是在堆区再开辟一块新的内存,两个相同数据不再指向同一片内存。这样就不会重复释放了
完整代码如下:
#include<iostream>
using namespace std;
#include<string>
//深浅拷贝是面试经典问题,也是常见的一个坑
//
//
//
//浅拷贝:简单的赋值拷贝操作
//
//
//
//深拷贝:在堆区重新申请空间,进行拷贝操作
class Person {
public:
int age;
int* height; //身高,因为要在堆区开辟数据,所以设置为指针
Person() {
cout << "Person的默认构造函数被调用" << endl;
}
Person(int a,int b) {
age = a;
height = new int(b); //属性在堆区开辟内存,必须要写拷贝构造函数并使用深拷贝
cout << "Person的有参构造函数被调用" << endl;
}
// 为了解决浅拷贝的问题,得自己写个拷贝构造函数,并使用深拷贝
Person(const Person& p) {
age = p.age;
// 有参函数创建堆区后,编译器默认写的一句话是 height=p.height;这是浅拷贝,会引起中断(堆区被重复释放),因为浅拷贝就是简单的复制粘贴,
// 深拷贝
height = new int (*p.height);
// 这句话的意思是:在堆区开辟一块内存,内存中的数据为*p.height,用height指针指向他
cout << "Person的拷贝构造函数被调用" << endl;
}
~Person() {
// 析构代码,将堆区开辟数据做释放操作
if (height != NULL) {
delete height;
height = NULL;
}
cout << "Person的析构函数被调用" << endl;
}
};
void test01() {
Person p1(18,178);
cout << "p1的年龄为" << p1.age <<" "<<"身高为:"<<*p1.height<< endl;
Person p2(p1);
cout << "p2的年龄为" << p2.age << " " << "身高为:" << *p2.height << endl;
}
int main() {
test01();
system("pause");
return 0;
}
总结:如果属性有在堆区开辟的,且涉及到了拷贝构造时,一定要自己提供一个新的拷贝构造函数,防止浅拷贝带来的问题