C&C++ 深拷贝与浅拷贝

在 C&C++ 中,深拷贝(Deep Copy)浅拷贝(Shallow Copy) 是对象复制的两种根本不同的方式,主要区别在于如何处理指针成员指向的资源。理解它们的区别对内存管理和程序稳定性至关重要。

关于C的深拷贝与浅拷贝 c语言中的深拷贝和浅拷贝 - 知乎

推荐博文

c语言中的深拷贝和浅拷贝 - 知乎

C语言 - 深拷贝与浅拷贝详解_c语言 deepcopy-CSDN博客

【C++ 深拷贝与浅拷贝详解】_c++浅拷贝和深拷贝-CSDN博客

【C++ Primer】 浅拷贝和深拷贝的实现 - 知乎


1. 浅拷贝(Shallow Copy)

原理:仅复制对象的成员值(包括指针的值),不复制指针指向的实际资源
结果:原对象和拷贝对象的指针成员指向同一块内存地址

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Shallow {
public:
    int* data;
    
    Shallow(int d) {
        data = new int(d);  // 分配堆内存
    }
    
    // 编译器生成的默认拷贝构造函数(浅拷贝)
    Shallow(const Shallow& other) : data(other.data) {} 
    
    ~Shallow() { delete data; }
};

// 使用示例
Shallow obj1(10);
Shallow obj2 = obj1;  // 浅拷贝:obj2.data 指向 obj1.data 的内存

*obj1.data = 20;      // 修改 obj1
cout << *obj2.data;   // 输出 20(obj2 也被修改)

// main结束时:obj2 和 obj1 会先后调用析构函数
// 导致同一内存被 delete 两次 → 程序崩溃!

浅拷贝的核心问题:

  1. 悬空指针:一个对象释放资源后,另一个对象的指针失效
  2. 重复释放:多个对象尝试释放同一块内存 → 崩溃
  3. 数据篡改:通过一个对象修改数据会影响其他对象

2. 深拷贝(Deep Copy)

原理:不仅复制对象成员,还复制指针指向的资源
结果:原对象和拷贝对象拥有完全独立的资源副本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Deep {
public:
    int* data;
    
    Deep(int d) {
        data = new int(d);
    }
    
    // 自定义拷贝构造函数(深拷贝)
    Deep(const Deep& other) {
        data = new int(*other.data);  // 关键:分配新内存并复制值
    }
    
    // 深拷贝赋值运算符
    Deep& operator=(const Deep& other) {
        if (this != &other) {  // 防止自赋值
            delete data;              // 释放原有资源
            data = new int(*other.data);  // 创建新副本
        }
        return *this;
    }
    
    ~Deep() { delete data; }
};

// 使用示例
Deep obj1(10);
Deep obj2 = obj1;  // 深拷贝:obj2 拥有独立的内存副本

*obj1.data = 20;   // 修改 obj1
cout << *obj2.data; // 输出 10(obj2 不受影响)

// 析构时各自释放独立内存 → 安全

深拷贝的核心优势:

  1. 资源独立:每个对象拥有自己的资源副本
  2. 安全析构:不会发生重复释放
  3. 数据隔离:修改一个对象不影响其他对象

3. 关键对比表

特性 浅拷贝 深拷贝
复制内容 指针值(内存地址) 指针指向的实际数据
资源副本 共享同一份资源 创建独立资源副本
内存开销 大(需额外分配内存)
安全性 危险(悬空指针/重复释放) 安全
默认行为 编译器默认生成 需要手动实现
修改影响 影响所有副本 只影响当前对象
适用场景 无资源管理的简单对象 含指针/动态资源的对象

4. 何时需要深拷贝?

当类满足以下任一条件时必须实现深拷贝

  1. 包含原始指针成员指向动态分配的内存
  2. 管理独占资源(文件句柄、网络连接等)
  3. 需要独立副本逻辑(如字符串、容器类)

Rule of Three 原则:如果类需要自定义析构函数,则通常也需要自定义拷贝构造函数和拷贝赋值运算符(即深拷贝)。

5. 现代C++的优化方案

(1) 禁用拷贝

1
2
3
4
5
6
class NonCopyable {
public:
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;  // 禁用拷贝
    NonCopyable& operator=(const NonCopyable&) = delete;
};

(2) 使用智能指针(推荐)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <memory>
class SafeObject {
    std::unique_ptr<int> data;  // 独占所有权
public:
    SafeObject(int d) : data(std::make_unique<int>(d)) {}
    
    // 不需要自定义拷贝构造/赋值
    // unique_ptr 禁止拷贝,但支持移动
};

// shared_ptr 版本(共享所有权,引用计数)
class SharedResource {
    std::shared_ptr<MyResource> res;
};

(3) 移动语义(C++11+)

1
2
3
4
5
6
7
8
9
class Movable {
    int* data;
public:
    // 移动构造函数(转移资源所有权)
    Movable(Movable&& other) noexcept 
        : data(other.data) {
        other.data = nullptr;  // 置空原对象
    }
};

6. 深拷贝的典型应用场景

  1. 字符串类std::string 内部实现深拷贝
  2. 容器类std::vector 复制时深拷贝所有元素
  3. 自定义资源管理:数据库连接池、图像缓冲区等
  4. 多线程安全:避免共享资源需要深拷贝

总结

概念 本质 解决方案
浅拷贝 复制指针(共享资源) 编译器默认行为
深拷贝 复制资源内容(独立副本) 自定义拷贝构造/赋值运算符
现代实践 避免手动深拷贝 使用智能指针+移动语义

最佳实践

  1. 优先使用 std::vector, std::string 等已安全实现的容器
  2. std::unique_ptr/std::shared_ptr 替代原始指针
  3. 遵循 Rule of Zero:通过组合安全类型避免自定义拷贝/析构
  4. 对于必须深拷贝的大型对象,考虑添加移动语义优化性能
experience
使用 Hugo 构建
主题 StackJimmy 设计