C++ 浅拷贝和深拷贝与坑

这篇博客探讨了C++中由于默认浅拷贝构造函数导致的问题,即对象析构时的多次内存释放。作者通过一个简单的Phone类实例,解释了浅拷贝如何引发崩溃,并提出了深拷贝作为解决方案。文章强调了对于含有动态分配内存的类,应自定义拷贝构造函数以避免此类问题。
摘要由CSDN通过智能技术生成

前言

已从工作三年的公司离职,今天我久违的徒弟惨叫着发来一个bug……看到之后,我有些沉默。我怀疑不帮他,微信铃声可能会一直响个不停。

这个问题有些基础,但也是c++新手容易犯的错误之一

问题

由于代码保密,这里简单的抽象出一个demo,简单说明下问题

class Phone
{
public:
    Phone()
    {
        mBrand = new char(20);
    }

    ~Phone()
    {
        if (mBrand != NULL)
        {
            // p2执行时因为多次释放崩溃
            delete mBrand;
            mBrand = NULL;
        }
    }

private:
    //品牌
    char *mBrand;
}

int main()
{
    Phone p1;
    //这里使用了编译器提供的默认的拷贝构造
    Phone p2(p1);
    return 0;
}

徒弟在使用了编译器提供的默认拷贝构造函数,在这些完方法,要对Phone对象进行析构时,对mBrand进行了多次释放导致崩溃

解析

为什么会出现这个问题?

浅拷贝

当我们未定义拷贝构造函数,系统则会调用默认拷贝构造函数。这个拷贝函数做的事就是使用同一类中之前创建的对象来初始化新创建的对象。

于是这里的这段代码我们可以这样补充出来

    Phone(const Phone &p) //系统创建出的一个拷贝构造
    {
        mBrand = p.mBrand;
    }

我们根据代码和图示,说明一下步骤:

1. Phone p1触发了无参构造 ,为mBrand在堆上开辟了地址未0x0012的内存空间,这个地址上的值是char(20);

2. Phone p2(p1)触发了系统的默认拷贝构造函数,根据上面补充出来的代码,我们知道,p2的mBrand指针也是指向了0x0012;

3. 当main函数执行完,p2开始析构,因为mBrand=0x0012,不为NULL,所以对0x0012上的数据即char(20)进行了释放操作;

4. 接着p1开始析构,因为他自己的成员变量mBrand=0x0012,不为NULL,所以再对0x0012上的数据即char(20)进行了释放操作,但是因为步骤3中已经释放过一次了,所以会触发多次释放的崩溃错误;

解决方法

深拷贝

针对以上情况,我们可以重新在堆申请一块内存0x0022,让这块儿内存同样保存值char(20),这样不同的对象p1,p2的mBrand都是指向不同的地址0x0012,0x0022。就可以在保证储存值相同的情况下,也不怕导致同一块内存被重复释放。

Phone(const Phone &p)
{
    //深拷贝
	mBrand = new char(20);
	memcpy(mBrand, p.mBrand, strlen(p.mBrand));
}

注:

以上代码仅为说明问题,临时缩减的demo 代码,可能并不合理严谨,仅供分析问题使用。

总结

如果类中属性有在堆中申请内存空间的,务必自己提供拷贝构造,处理好浅拷贝所带来的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值