C++ 学习(6)——浅复制与深复制

理解 C++ 中的浅复制与深复制:本质、区别与实践

在 C++ 中进行对象赋值或传参时,常常涉及“复制”操作。但复制并非简单的值传递——它背后隐藏着一对关键概念:浅复制(Shallow Copy)与深复制(Deep Copy)

这两个概念对于管理资源、避免内存泄漏或悬挂指针等问题至关重要。本文将从本质概念、代码示例、内存模型、何时使用等方面全面解读。


一、复制的本质:值的复制 vs 指针的复制

在 C++ 中,默认的复制构造函数和赋值操作符执行的是浅复制,即对每个成员变量进行“逐位复制”。对于普通数据类型这没有问题,但当类中包含指针成员时,就必须谨慎对待。


二、什么是浅复制(Shallow Copy)?

浅复制是默认行为,它简单地复制对象中所有成员的值,包括指针本身的值(即内存地址),而不会复制指针所指向的内容。

🔸 示例:

class Shallow {
public:
    int* data;

    Shallow(int val) {
        data = new int(val);
    }

    // 使用默认的复制构造函数(浅复制)
    ~Shallow() {
        delete data;
    }
};

int main() {
    Shallow obj1(10);
    Shallow obj2 = obj1;  // 浅复制

    *obj2.data = 20;
    std::cout << *obj1.data << std::endl;  // 输出 20
}

🔸 问题:

  • obj1obj2data 指向 相同的地址
  • obj1obj2 被析构时,同一块内存被释放两次,导致程序崩溃(double free)

三、什么是深复制(Deep Copy)?

深复制是对对象中所有资源的完全复制。对于指针,深复制会分配新的内存并复制原始数据内容,两个对象拥有独立的数据副本。

✅ 示例:

class Deep {
public:
    int* data;

    Deep(int val) {
        data = new int(val);
    }

    // 自定义复制构造函数(深复制)
    Deep(const Deep& other) {
        data = new int(*other.data);  // 分配新内存,复制值
    }

    ~Deep() {
        delete data;
    }
};

int main() {
    Deep obj1(10);
    Deep obj2 = obj1;  // 深复制

    *obj2.data = 20;
    std::cout << *obj1.data << std::endl;  // 输出 10
}

✅ 优点:

  • 避免双重释放问题。
  • 两个对象互不干扰,内存安全。

四、深复制需要实现哪些函数?

当类中包含指针成员时,必须手动实现如下三大函数(俗称“三五法则”或 Rule of Three):

class MyClass {
private:
    int* data;

public:
    // 构造函数
    MyClass(int val) {
        data = new int(val);
    }

    // 自定义复制构造函数
    MyClass(const MyClass& other) {
        data = new int(*other.data);
    }

    // 自定义赋值操作符
    MyClass& operator=(const MyClass& other) {
        if (this == &other) return *this;  // 自赋值检查
        delete data;
        data = new int(*other.data);
        return *this;
    }

    // 析构函数
    ~MyClass() {
        delete data;
    }
};

五、浅复制 vs 深复制:对比表格

项目 浅复制 深复制
拷贝内容 指针地址(引用同一资源) 分配新内存并复制数据
数据独立性
内存管理风险 高(易 double delete)
系统默认行为 否(需自定义构造/赋值函数)
适用场景 资源共享但只读访问 独立资源或需要修改数据副本

六、何时需要深复制?

你应当在以下场景主动实现深复制:

  • 类中包含动态分配的资源(如 new 分配的数组、结构体、句柄等)。
  • 对象之间需要独立维护资源(比如图像拷贝、缓存数据等)。
  • 存在生命周期交叉所有权不清晰的指针数据。

七、现代 C++ 替代方案(推荐)

从 C++11 开始,推荐使用智能指针(如 std::unique_ptr, std::shared_ptr)管理资源,避免手动复制和释放指针资源:

#include <memory>

class Smart {
    std::unique_ptr<int> data;

public:
    Smart(int val) : data(std::make_unique<int>(val)) {}

    // 自动调用移动构造函数或拷贝构造(视 unique/shared 而定)
};

八、总结

  • 浅复制是系统默认行为,快速但可能引发内存问题;
  • 深复制通过显式实现复制构造函数与赋值运算符,确保资源独立;
  • 理解并区分这两者对于 C++ 中的资源管理至关重要;
  • 在现代 C++ 中,使用智能指针更安全可靠,大幅减少错误和代码复杂度。
posted @ 2025-07-13 10:21  seekwhale13  阅读(133)  评论(0)    收藏  举报