用代码分析深拷贝和浅拷贝的区别,利用深拷贝解决浅拷贝的问题。
目录
代码
#include <iostream>
#include <string>
using namespace std;
class Person{
public:
int s_age;
int *m_hight;
Person(){
cout<<"无参构造函数"<<endl;
}
Person(int age,int height){
cout<<"有参构造函数"<<endl;
s_age = age;
//从堆区申请一块内存 m_hight 指针指向该内存
m_hight = new int(height);
}
Person(const Person &p){
cout<<"拷贝构造函数"<<endl;
s_age = p.s_age;
//编译器默认实现这个代码 拷贝的只是一个值,但指针指向的还是原来的堆区,并没有重写创建
//m_hight = p.m_hight
//所以需要再利用new 重新创建内存
m_hight = new int(*p.m_hight);
}
~Person(){
//根据栈区先进后出原则,p2先释放 进入析构函数
if(m_hight!=NULL){
delete m_hight; //释放堆区的内存
m_hight = NULL;
}
cout<<"析构函数"<<endl;
}
};
void test1(){
Person p1(20,180);
cout<<"p1_age:"<<p1.s_age<<" p1_hight:"<<*p1.m_hight<<endl;
Person p2(p1);
cout<<"p2_age:"<<p2.s_age<<" p2_hight:"<<*p2.m_hight<<endl;
}
int main()
{
test1();
return 0;
}
浅拷贝的问题
我们创建第一个对象p1的时候,利用构造函数向内存申请一个堆区的内存,这里叫他 memory1,然后再利用拷贝构造函数,创建一个p2,如果利用浅拷贝(即编辑器自己创建),m_hight = p.m_hight实现,只会让p2 的 m_hight 指针指向 p1的 memory1,如下图。最终会导致析构函数执行的时候,根据先入后出的原则,p2会先释放memory1的内存,p1再去释放的时候,该内存已经被释放掉了,属于非法操作,堆区内存重复释放,这是浅拷贝带来的问题。
深拷贝解决
若要避免浅拷贝带来的问题,需要重写拷贝函数,我们在拷贝构造函数中重新去申请一块内存,和构造函数一样。
Person(const Person &p){ cout<<"拷贝构造函数"<<endl; s_age = p.s_age; //m_hight = p.m_hight 编译器默认实现这个代码 m_hight = new int(*p.m_hight); }
之后呢,p2的 m_hight 不再指向p1的内存(memory1),p2有自己的内存,这样释放的时候,各自释放各自的堆区内存空间,不再有冲突。这里的p2创建一个新的堆区内存空间 memory2。p1和p2的m_hight指向不同的内存空间,这样执行析构不会出现内存重复释放的问题。