C++浅拷贝和深拷贝的区别

深浅拷贝是面试面试经典问题
C++中的构造函数有三类,默认构造函数,有参构造函数,拷贝构造函数。
首先考虑一种情况,对一个已经定义的对象进行拷贝,编译器会调用构造函数中的拷贝构造函数。

浅拷贝: 如果用户没有定义自己的拷贝构造函数,则会调用默认的拷贝构造函数。默认拷贝构造函数中会对对象中的变量进行简单的赋值拷贝操作,就是一个等号赋值操作,这种操作就是浅拷贝。
深拷贝: 而深拷贝就是在堆区重新申请一块空间,来进行拷贝赋值操作。
在下面的程序中,我们先定义一个dog1对象,并将dog1拷贝给dog2

#include <iostream>
using namespace std;

class dog{
public:
	int d_Age;
	dog(){
		cout <<"dog的默认构造函数" << endl;
}
	dog(int age){
	    d_Age = age;
		cout <<"dog的有参构造函数" << endl;
}
    ~dog(){
		cout <<"dog的析构函数" << endl;
}
};

int main(){
    
	dog dog1(5);   //调用有参构造函数
	cout << "dog1的年龄为:" << dog1.d_Age << endl;
	dog dog2(dog1);  //调用默认拷贝构造函数
	cout << "dog2的年龄为:" << dog2.d_Age << endl;

}

由于这个时候我们没有自定义拷贝构造函数,所以编译器调用了默认的拷贝构造函数,也就是完成了浅拷贝,将dog1的年龄拷贝给了dog2。函数执行结果:

dog的有参构造函数  
dog1的年龄为:5
dog2的年龄为:5
dog的析构函数
dog的析构函数

此时我们增加dog大小的变量d_Size,我们将变量定义在堆区,堆区的变量需要我们手动释放,所以在析构函数中我们需要将堆区开辟的数据释放。运行下方程序

#include <iostream>
using namespace std;

class dog{
public:
	int d_Age;
	int * d_Size;
	
	dog(){
		cout <<"dog的默认构造函数" << endl;
}
	dog(int age,int sizze){
	    d_Age = age;
	    d_Size = new int(sizze); //返回的是一个指针
		cout <<"dog的有参构造函数" << endl;
}
    ~dog(){
        if(d_Size != NULL){  //释放堆区数据
            delete d_Size;
            d_Size = NULL;
        }
		cout <<"dog的析构函数" << endl;
}
};

int main(){
    
	dog dog1(5,40);   //调用有参构造函数
	cout << "dog1的年龄为:" << dog1.d_Age <<"大小为:" << *dog1.d_Size << endl;
	dog dog2(dog1);  //调用默认拷贝构造函数
	cout << "dog2的年龄为:" << dog2.d_Age <<"大小为:" << *dog2.d_Size << endl;

}

这时候程序崩了。。。
在这里插入图片描述
为什么会这样子呢?我们刚才创建了两个对象,dog1和dog1的复制品dog2,如果利用编译器提供的拷贝构造函数,会做浅拷贝操作。也就是把dog1的的d_Age和dog1的d_Size复制给dog2,这里d_Size是我们在堆区用new创建的一个变量,所以保存的是一个地址。浅拷贝会直接将dog1中的d_Age(保存的是5)和d_Size(保存的是变量的地址)中的值复制给dog2,所以dog2中的d_Age和d_Size保存的也就是5和变量的地址。当调用析构函数的时候,dog1和dog2都会执行析构函数,由于dog1和dog2是保存在栈中,先进后出,所以dog2先调用析构函数,先将d_Size中的地址对应的堆区中的内存释放,然后dog1再执行析构函数中释放内存操作,由于同一块地址的内存已经被释放过了,所以程序就崩了。如下图
在这里插入图片描述
浅拷贝带来的问题就是堆区的内存重复释放。 浅拷贝的问题,需要利用深拷贝进行解决。由于编译器提供的浅拷贝不能满足要求,我们要自己实现一个拷贝构造函数,来解决浅拷贝带来的问题。

#include <iostream>
using namespace std;

class dog{
public:
	int d_Age;
	int * d_Size;
	
	dog(){
		cout <<"dog的默认构造函数" << endl;
}
	dog(int age,int dsize){
	    d_Age = age;
	    d_Size = new int(dsize); //返回的是一个指针
		cout <<"dog的有参构造函数" << endl;
}
	dog(const dog &p){
		cout << "dog的拷贝构造函数"<< endl;
		d_Age = p.d_Age;
		d_Size = new int(*p.d_Size);
}
    ~dog(){
        if(d_Size != NULL){  //释放堆区数据
            delete d_Size;
            d_Size = NULL;
        }
		cout <<"dog的析构函数" << endl;
}
};

int main(){
    
	dog dog1(5,40);   //调用有参构造函数
	cout << "dog1的年龄为:" << dog1.d_Age <<"大小为:" << *dog1.d_Size << endl;
	dog dog2(dog1);  //调用拷贝构造函数
	cout << "dog2的年龄为:" << dog2.d_Age <<"大小为:" << *dog2.d_Size << endl;

}

这次程序没有崩掉,完美运行~ 由于我们在拷贝构造函数中重新开辟了堆区的内存,则变量结构如下

dog的有参构造函数
dog1的年龄为:5大小为:40
dog的拷贝构造函数
dog2的年龄为:5大小为:40
dog的析构函数
dog的析构函数

在这里插入图片描述
总结:浅拷贝就是编译器实现的直接等号复制操作,深拷贝重新申请一块空间,进行拷贝操作,所以如果对象中有属性在堆区中开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值