c++深拷贝和浅拷贝

【1】浅拷贝
一直以来,设计一个类,个人认为,最能体现水平的地方在于:类中含有指针成员变量。

如下一个典型的浅拷贝示例:

#include <iostream>
using namespace std;

class HasPtrMem
{
public:
    HasPtrMem() : d(new int(0))
    {}
    ~HasPtrMem()
    {
        delete d;
        d = nullptr;
    }

    int* d;
};

int main()
{
    HasPtrMem a;
    HasPtrMem b(a);

    cout << *a.d << endl; // 0
    cout << *b.d << endl; // 0
} // 异常析构

定义了一个含有指针成员变量d的类型HasPtrMem。

该成员d在构造时会接受一个new操作分配堆内存返回的指针,而在析构的时候则会被delete操作用于释放分配的堆内存。

在main函数中,声明了HasPtrMem类型的对象a,又使用a初始化了对象b。按照C++的语法,这会调用HasPtrMem的拷贝构造函数。

而这里的拷贝构造函数由编译器隐式生成,其作用是执行类似于memcpy的按位拷贝。

这样的构造方式有一个问题,就是a.d和b.d都指向了同一块堆内存。

因此在main作用域结束的时候,对象b和对象a的析构函数会分别依次被调用。

当其中之一完成析构之后(比如对象b先析构,b.d先被delete),那么a.d就成了一个“悬挂指针”(dangling pointer),因为其不再指向有效的内存了。

那么在该悬挂指针上释放内存就会造成严重的错误。

【2】深拷贝
通常情况下,在类中未声明构造函数的情况下,C++也会为类生成一个“浅拷贝”(shollow copy)的构造函数。

而最佳的解决方案是用户自定义拷贝构造函数来实现“深拷贝”(deep copy),修正上例为深拷贝的结果:

#include <iostream>
using namespace std;

class HasPtrMem
{
public:
    HasPtrMem() : d(new int(0))
    {}
    HasPtrMem(const HasPtrMem& h) : d(new int(*h.d)) { } // 拷贝构造函数,从堆中分配内存,并用*h.d初始化
    ~HasPtrMem()
    {
        delete d;
        d = nullptr;
    }

    int* d;
};

int main()
{
    HasPtrMem a;
    HasPtrMem b(a);

    cout << *a.d << endl; // 0
    cout << *b.d << endl; // 0
} // 正常析构

为类HasPtrMem添加了一个拷贝构造函数。

拷贝构造函数从堆中分配新内存,将该分配来的内存的指针交还给d,又使用*(h.d)对*d进行了初始化。

通过这样的方法,就避免了悬挂指针的困扰。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值