深拷贝与浅拷贝的区别
深拷贝和浅拷贝本质的区别在于浅拷贝获取的是原对象的引用,深拷贝获取的是原对象的复制实体。
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
为什么要使用深拷贝
C++编译器默认的拷贝构造函数都是浅拷贝。浅拷贝在类中存在指针的时候,拷贝的是原对象的引用,这会带来一个问题:
在对象退出的时候,会调用析构函数对申请的堆内存进行释放,如果是默认的浅拷贝,两次析构函数会对同一个空间进行释放,这会报错。
为了避免浅拷贝的这个问题,必须使用深拷贝:拷贝构造函数重新申请一个内存空间,复制原对象。这样释放的时候就可以都释放了。
实例
class Person {
public:
Person():age(10)
{
cout << "person的无参构造函数" << endl;
}
Person(int a, int height) {
age = a;
m_height = new int(height);
cout << "Person的有参构造函数" << endl;
}
//拷贝构造函数
Person(const Person &p) {
age = p.age;
//深拷贝:重新申请一个堆,内容复制原对象
m_height = new int(*p.m_height);
cout << "Person的拷贝构造函数" << endl;
}
//析构函数
~Person() {
cout << "person的析构函数" << endl;
//对申请的堆进行释放
if (m_height != NULL) {
delete m_height;
m_height = NULL;
}
}
int age;
int *m_height;
};
//--------------深拷贝与浅拷贝----------------
void test1() {
Person p1(16, 180);
cout << "年龄为:" << p1.age << "\t身高为:" << *p1.m_height << "\t地址为:" << (int*)&p1.m_height << endl;
Person p2(p1);
cout << "年龄为:" << p2.age << "\t身高为:" << *p2.m_height << "\t地址为:" << (int*)&p2.m_height << endl;
}
这里的拷贝构造函数Person(const Person &p)
需要我们自己定义成深拷贝形式(C++编译器默认的是浅拷贝:m_height = p.m_height;
)。
如果是浅拷贝,test1()在拷贝构造p2的时候,会直接把p2.m_height = p1.m_height
,p1,p2指向了同一个堆的区域。在test1()结束后,会先调用p1的析构函数,把堆中的空间释放掉;然后调用p2的析构函数,又想去释放这个已经释放的空间,这就会出错。
这就需要改为深拷贝,new int(*p.m_height)
,重新在堆中申请一块和原对象一模一样的空间,这样两次析构函数就会分别去释放p1,p2申请的空间,就不会出错了。
可以看到结果,p1,p2申请的m_height的堆空间不同,就可以分别调用析构函数了。