C++中的浅拷贝和深拷贝

1 什么是浅拷贝和深拷贝

浅拷贝(Shallow Copy)

浅拷贝是默认的拷贝行为,它仅复制对象的成员变量的值。如果对象中包含指针,浅拷贝只会复制指针的值,而不是指针所指向的内存。这意味着原始对象和拷贝对象将共享同一块内存。

另一种解释:浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

深拷贝(Deep Copy)

深拷贝会为拷贝对象创建一个新的内存副本,即使是对象中的指针也指向新的内存。这样,原始对象和拷贝对象就不会共享内存,它们是完全独立的。

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

2 浅拷贝和深拷贝的区别

下面是浅拷贝:

class ShallowCopyExample {
public:
    int* data; // 指针指向动态分配的内存
    ShallowCopyExample(int size) {
        data = new int[size];
    }
    // 拷贝构造函数
    ShallowCopyExample(const ShallowCopyExample& other) {
        data = other.data; // 仅复制指针
    }
    ~ShallowCopyExample() {
        delete data;
    }
};

使用浅拷贝时,如果原始对象在拷贝后被销毁,那么拷贝对象中的指针将指向一块已经被释放的内存,这将导致未定义行为。

下面是深拷贝:

class DeepCopyExample {
public:
    int* data; // 指针指向动态分配的内存
    DeepCopyExample(int size) {
        data = new int[size];
    }
    // 拷贝构造函数
    DeepCopyExample(const DeepCopyExample& other) {
        data = new int[other.size]; // 为拷贝对象分配新的内存
        std::copy(other.data, other.data + other.size, data); // 复制内容
    }
    ~DeepCopyExample() {
        delete[] data;
    }
};

深拷贝确保了拷贝对象的独立性,即使原始对象被销毁,拷贝对象也能正常工作。

3 string类中的体现

假设有string类的模拟实现如下:

// 为了和标准库区分,此处使用String
class String
{
public:
/*String()
 :_str(new char[1])
 {*_str = '\0';}
 */
 //String(const char* str = "\0") 错误示范
 //String(const char* str = nullptr) 错误示范
 String(const char* str = "")
 {
     // 构造String类对象时,如果传递nullptr指针,可以认为程序非
     if (nullptr == str)
     {
         assert(false);
         return;
     }
     _str = new char[strlen(str) + 1];
     strcpy(_str, str);
 }
 ~String()
 {
     if (_str)
     {
         delete[] _str;
         _str = nullptr;
     }
 }
private:
 char* _str;
};

我们使用如下代码进行测试:

void TestString()
{
    String s1("hello bit!!!");
    String s2(s1);
}

我们可以发现,上述的模拟实现是我们所说的浅拷贝 :

也就是指针指向同一块空间,在销毁时会重复销毁导致野指针的问题。也就是说,上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。 

那么我们需要显式给出拷贝构造,析构函数以及赋值运算符。

下面给出传统写法的string类

class String
{
public:
    String(const char *str = "")
    {
        // 构造String类对象时,如果传递nullptr指针,可以认为程序非
        if (nullptr == str)
        {
            assert(false);
            return;
        }
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
    String(const String &s)
        : _str(new char[strlen(s._str) + 1])
    {
        strcpy(_str, s._str);
    }
    String &operator=(const String &s)
    {
        if (this != &s)
        {
            char *pStr = new char[strlen(s._str) + 1];
            strcpy(pStr, s._str);
            delete[] _str;
            _str = pStr;
        }
        return *this;
    }
     ~String()
    {
        if (_str)
        {
            delete[] _str;
            _str = nullptr;
        }
    }

private:
    char *_str;
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值