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

通过对比深拷贝与浅拷贝的核心差异、具体行为、典型问题及使用场景,来进一步了解深浅拷贝。

一、核心概念对比

特性浅拷贝(Shallow Copy)深拷贝(Deep Copy)
定义仅复制对象的成员变量值(包括指针地址)复制对象的成员变量值,且为动态资源重新分配内存并复制内容
资源管理多个对象共享同一块动态资源(如堆内存)每个对象拥有独立的动态资源
风险可能导致悬垂指针(资源被释放后其他对象仍引用)、内存泄漏(重复释放)无共享资源问题,更安全

二、浅拷贝的典型问题:以动态内存为例

假设一个类 MyClass 包含动态分配的内存(如 int* data),默认的拷贝构造函数和赋值运算符(编译器自动生成)会执行浅拷贝

示例代码(浅拷贝的问题)
#include <iostream>

class ShallowCopyDemo {
private:
    int* data;  // 动态分配的内存指针

public:
    // 构造函数:分配内存并初始化
    ShallowCopyDemo(int value) {
        data = new int(value);  // 动态分配内存
        std::cout << "构造函数:分配内存,地址=" << data << std::endl;
    }

    // 默认拷贝构造函数(浅拷贝,编译器自动生成)
    // ShallowCopyDemo(const ShallowCopyDemo& other) = default;

    // 析构函数:释放内存
    ~ShallowCopyDemo() {
        if (data != nullptr) {
            delete data;
            std::cout << "析构函数:释放内存,地址=" << data << std::endl;
            data = nullptr;  // 防止悬垂指针
        }
    }

    // 辅助函数:修改数据并打印地址
    void setValue(int value) {
        *data = value;
        std::cout << "当前 data 地址=" << data << ",值=" << *data << std::endl;
    }
};

int main() {
    ShallowCopyDemo a(10);  // 构造a,data指向0x1234(假设地址)
    ShallowCopyDemo b = a;  // 浅拷贝:b.data = a.data(地址相同)

    a.setValue(20);  // 修改a.data的值 → b.data的值也会被修改!
    b.setValue(30);  // 修改b.data的值 → a.data的值也会被修改!

    return 0;  // 程序结束时,a和b的析构函数会**重复释放同一块内存**,导致崩溃
}
浅拷贝的问题总结
  1. 共享资源a.datab.data 指向同一块内存(地址相同)。
  2. 数据污染:修改 a.data 会直接影响 b.data(反之亦然)。
  3. 重复释放:析构时 ab 都会尝试释放同一块内存,导致程序崩溃(未定义行为)。

三、深拷贝的解决方案:手动管理资源

要避免浅拷贝的问题,需显式定义拷贝构造函数和拷贝赋值运算符,为动态资源重新分配内存并复制内容。

示例代码(深拷贝的实现)
#include <iostream>

class DeepCopyDemo {
private:
    int* data;  // 动态分配的内存指针

public:
    // 构造函数:分配内存并初始化
    DeepCopyDemo(int value) {
        data = new int(value);
        std::cout << "构造函数:分配内存,地址=" << data << std::endl;
    }

    // 拷贝构造函数:深拷贝实现
    DeepCopyDemo(const DeepCopyDemo& other) {
        // 为当前对象分配新内存(与原对象独立)
        data = new int(*other.data);  // 复制原对象data指向的值
        std::cout << "拷贝构造:分配新内存,地址=" << data << std::endl;
    }

    // 拷贝赋值运算符:深拷贝实现
    DeepCopyDemo& operator=(const DeepCopyDemo& other) {
        // 处理自赋值(如 a = a)
        if (this == &other) return *this;

        // 释放当前对象的旧内存(避免内存泄漏)
        delete data;

        // 分配新内存并复制原对象的值
        data = new int(*other.data);
        std::cout << "赋值运算符:分配新内存,地址=" << data << std::endl;

        return *this;
    }

    // 析构函数:释放内存
    ~DeepCopyDemo() {
        if (data != nullptr) {
            delete data;
            std::cout << "析构函数:释放内存,地址=" << data << std::endl;
            data = nullptr;
        }
    }

    // 辅助函数:修改数据并打印地址
    void setValue(int value) {
        *data = value;
        std::cout << "当前 data 地址=" << data << ",值=" << *data << std::endl;
    }
};

int main() {
    DeepCopyDemo a(10);  // a.data指向0x1234(假设地址)
    DeepCopyDemo b = a;  // 深拷贝:b.data指向0x5678(新地址)
    DeepCopyDemo c(20);
    c = a;               // 深拷贝:c.data指向0x9abc(新地址)

    a.setValue(20);  // 仅修改a.data的值(地址0x1234)
    b.setValue(30);  // 仅修改b.data的值(地址0x5678)
    c.setValue(40);  // 仅修改c.data的值(地址0x9abc)

    return 0;  // 各对象独立释放自己的内存,无冲突
}
深拷贝的关键行为
  1. 独立内存:每个对象的 data 指针指向不同的内存地址(如 a.data0x1234b.data0x5678)。
  2. 数据隔离:修改 a.data 的值不会影响 b.datac.data
  3. 安全释放:每个对象的析构函数仅释放自己分配的内存,无重复释放问题。

四、深拷贝的使用场景

当类中包含以下动态资源时,必须使用深拷贝(否则默认浅拷贝会导致严重问题):

  1. 堆内存:通过 new/malloc 动态分配的内存(如示例中的 int* data)。
  2. 文件句柄:打开的文件描述符(如 FILE*)。
  3. 网络连接:套接字(socket)或数据库连接。
  4. 其他共享资源:如互斥锁、线程句柄等需要独占的资源。

五、笔记总结(可直接记录)

对比项浅拷贝深拷贝
核心复制指针地址(共享资源)复制资源内容(独立资源)
实现方式编译器自动生成(默认行为)手动定义拷贝构造函数+赋值运算符
风险数据污染、重复释放、内存泄漏无(需正确管理资源)
使用场景对象无动态资源(如纯数值成员)对象包含动态资源(如堆内存、文件句柄)

通过这种对比和示例,应该能更清晰理解深拷贝的原理和必要性了。如果需要进一步验证,可以运行上面的代码观察内存地址变化~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值