C++浅拷贝与深拷贝

本文通过实例解析了C++中浅拷贝和深拷贝的区别,强调了堆空间拷贝的重要性。浅拷贝仅复制基本类型成员,而深拷贝则涉及堆内存的复制,防止内存泄漏。当类中包含动态分配的资源时,必须实现深拷贝以确保正确性。同时,文章探讨了不恰当的拷贝构造函数可能导致的问题及解决方法,提醒开发者注意内存管理。
摘要由CSDN通过智能技术生成
浅拷贝与深拷贝最容易区别的地方在于堆空间的拷贝
浅拷贝:也是默认拷贝构造函数,在没有定义拷贝构造函数时编译器会自动生成一个默认的拷贝构造函数。
#include <iostream>
using namespace std;

class A
{
    int num;
public:
    A(int n) :num(n) {}
    ~A() {}
    //浅拷贝
    A(const A& a)
    {
        num = a.num;
    }
    void GetNum()
    {
        cout << "num = " << num << endl;
    }
};

int  main()
{
	A a1(2);
	a1.GetNum();
	A a2(a1);//等价于 A a2 = a1;
	a2.GetNum();
	
	return 0;
}

上例是一个很简单的浅拷贝,只有一个数据成员的赋值操作,编译器默认拷贝实现的也就是如上的效果,输出如下:

num = 2;
num = 2;

深拷贝:增加堆空间的new操作

如果仍使用浅拷贝构造函数会如何呢?如下所示:

#include <iostream>
using namespace std;

class A
{
    int num;
    int *p;//int型指针
public:
    A(int n) :num(n) 
    {
    	p = new int(n);//创建堆空间
    }
    ~A() 
    {
    	if(p != NULL)
    	{
    		delete p;
    	}
    }
    //浅拷贝
    A(const A& a)
    {
        num = a.num;
        p = a.p;//直接赋值
    }
    void GetNum()
    {
        cout << "num = " << num  << ", p = " << p << ", *p = " << *p << endl;
    }
};

int  main()
{
	A a1(2);
	a1.GetNum();
	A a2(a1);//等价于 A a2 = a1;
	a2.GetNum();
	
	return 0;
}

结果是能正常编译,但无法运行。
这是因为 a2 在拷贝 a1 的时候,指针(int *)p的地址也直接复制过来了;那么在程序结束,两个类调用两次析构函数 delete p; 但是其地址都是使用的同一个,就会重复释放造成错误。
这里可能会疑惑析构函数里的判断语句,第一次 delete p 之后 p 会不会等于NULL, 答案是不等于;原因是两个指针 p 都指向了一块地址,所以就算delete释放空间的值后,地址本身还被另一个p记录着,仍然会再次delete;

钻一下牛角尖:
如果去掉析构函数里的delete语句,结果确实可以正常运行。
但是,这种行为的性质是造成内存泄漏,是不安全的,不可取的。
用户创建堆空间的时候要记得手动释放。

那么针对堆内存的问题,如何实现深拷贝

只需要替换一下浅拷贝的代码:

//深拷贝
A(const A& a)
{
	num = a.num;
	p = new int(*a.p);
}

输出如下:

num = 2, p = 00CF55D8, *p = 2
num = 2, p = 00CF5608, *p = 2

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值