深度拷贝详解

什么是深度拷贝?

深度拷贝:创建一个新对象,并递归地复制原对象的所有成员(包括指针指向的内容),使得两个对象完全独立。

浅拷贝:只复制指针的值(地址),不复制指针指向的内容,导致两个对象共享同一块内存。

1. 基本的深度拷贝实现

自定义String类的深度拷贝示例

cpp 复制代码
#include <iostream>
#include <cstring>

class MyString {
private:
    char* m_data;    // 指向字符串的指针
    int m_length;    // 字符串长度
    
public:
    // 构造函数
    MyString(const char* str = "") {
        std::cout << "构造函数被调用" << std::endl;
        m_length = strlen(str);                          // 计算字符串长度
        m_data = new char[m_length + 1];                 // 动态分配内存(+1用于存放'\0')
        strcpy(m_data, str);                             // 复制字符串内容
    }
    
    // 拷贝构造函数(深度拷贝)
    MyString(const MyString& other) {
        std::cout << "拷贝构造函数被调用(深度拷贝)" << std::endl;
        m_length = other.m_length;                       // 复制长度
        m_data = new char[m_length + 1];                 // 重新分配内存
        strcpy(m_data, other.m_data);                    // 复制字符串内容
    }
    
    // 拷贝赋值运算符(深度拷贝)
    MyString& operator=(const MyString& other) {
        std::cout << "拷贝赋值运算符被调用(深度拷贝)" << std::endl;
        if (this != &other) {                            // 防止自赋值
            delete[] m_data;                             // 释放原有内存
            m_length = other.m_length;                   // 复制长度
            m_data = new char[m_length + 1];             // 重新分配内存
            strcpy(m_data, other.m_data);                // 复制字符串内容
        }
        return *this;                                    // 返回当前对象的引用
    }
    
    // 析构函数
    ~MyString() {
        std::cout << "析构函数被调用,释放内存: " << (void*)m_data << std::endl;
        delete[] m_data;                                 // 释放动态分配的内存
    }
    
    // 修改字符串内容
    void setString(const char* str) {
        delete[] m_data;                                 // 释放旧内存
        m_length = strlen(str);                          // 计算新长度
        m_data = new char[m_length + 1];                 // 分配新内存
        strcpy(m_data, str);                             // 复制新内容
    }
    
    // 获取字符串
    const char* getString() const {
        return m_data;
    }
    
    // 打印字符串地址和内容
    void printInfo(const char* name) const {
        std::cout << name << " - 地址: " << (void*)m_data 
                  << ", 内容: \"" << m_data << "\"" << std::endl;
    }
};

// 演示函数:展示拷贝行为
void demonstrateShallowCopyProblem() {
    std::cout << "=== 如果没有深度拷贝会发生什么 ===" << std::endl;
    
    // 假设我们使用浅拷贝(编译器默认生成的)
    // MyString str1("Hello");
    // MyString str2 = str1;  // 浅拷贝:两个对象指向同一块内存
    
    // 当str1修改内容时,str2也会受影响(非预期行为)
    // 当str1和str2析构时,同一块内存会被释放两次(程序崩溃)
}

int main() {
    std::cout << "=== 深度拷贝演示 ===" << std::endl;
    
    // 创建原始对象
    MyString original("Hello World");
    original.printInfo("original");
    
    std::cout << "\n--- 使用拷贝构造函数 ---" << std::endl;
    // 调用拷贝构造函数(深度拷贝)
    MyString copy1(original);
    copy1.printInfo("copy1");
    
    std::cout << "\n--- 使用拷贝赋值运算符 ---" << std::endl;
    // 调用拷贝赋值运算符(深度拷贝)
    MyString copy2("Temp");
    copy2 = original;
    copy2.printInfo("copy2");
    
    std::cout << "\n--- 修改原始对象验证独立性 ---" << std::endl;
    original.setString("Modified");
    original.printInfo("original");
    copy1.printInfo("copy1");    // copy1不受影响
    copy2.printInfo("copy2");    // copy2不受影响
    
    std::cout << "\n--- 作用域结束,自动调用析构函数 ---" << std::endl;
    // 所有对象离开作用域时,各自的析构函数会被调用
    // 每个对象释放自己独立的内存,不会出现重复释放
    
    return 0;
}

2. 复杂对象的深度拷贝

包含嵌套对象的深度拷贝

cpp 复制代码
#include <iostream>
#include <cstring>

class Person {
private:
    char* m_name;
    int m_age;
    
public:
    // 构造函数
    Person(const char* name, int age) : m_age(age) {
        m_name = new char[strlen(name) + 1];
        strcpy(m_name, name);
        std::cout << "Person构造函数: " << m_name << std::endl;
    }
    
    // 拷贝构造函数(深度拷贝)
    Person(const Person& other) : m_age(other.m_age) {
        m_name = new char[strlen(other.m_name) + 1];
        strcpy(m_name, other.m_name);
        std::cout << "Person拷贝构造函数: " << m_name << std::endl;
    }
    
    // 拷贝赋值运算符(深度拷贝)
    Person& operator=(const Person& other) {
        if (this != &other) {
            delete[] m_name;
            m_age = other.m_age;
            m_name = new char[strlen(other.m_name) + 1];
            strcpy(m_name, other.m_name);
        }
        std::cout << "Person拷贝赋值: " << m_name << std::endl;
        return *this;
    }
    
    // 析构函数
    ~Person() {
        std::cout << "Person析构函数: " << m_name << std::endl;
        delete[] m_name;
    }
    
    void setName(const char* name) {
        delete[] m_name;
        m_name = new char[strlen(name) + 1];
        strcpy(m_name, name);
    }
    
    void printInfo() const {
        std::cout << "姓名: " << m_name << ", 年龄: " << m_age << std::endl;
    }
};

class Department {
private:
    char* m_name;
    Person* m_manager;  // 指向Person对象的指针
    
public:
    // 构造函数
    Department(const char* deptName, const Person& manager) {
        m_name = new char[strlen(deptName) + 1];
        strcpy(m_name, deptName);
        
        // 深度拷贝:为新部门创建新的经理对象
        m_manager = new Person(manager);
        std::cout << "Department构造函数: " << m_name << std::endl;
    }
    
    // 拷贝构造函数(深度拷贝)
    Department(const Department& other) {
        // 复制部门名称
        m_name = new char[strlen(other.m_name) + 1];
        strcpy(m_name, other.m_name);
        
        // 深度拷贝经理对象
        m_manager = new Person(*(other.m_manager));
        std::cout << "Department拷贝构造函数: " << m_name << std::endl;
    }
    
    // 拷贝赋值运算符(深度拷贝)
    Department& operator=(const Department& other) {
        if (this != &other) {
            // 释放原有资源
            delete[] m_name;
            delete m_manager;
            
            // 复制新资源
            m_name = new char[strlen(other.m_name) + 1];
            strcpy(m_name, other.m_name);
            
            m_manager = new Person(*(other.m_manager));
        }
        std::cout << "Department拷贝赋值: " << m_name << std::endl;
        return *this;
    }
    
    // 析构函数
    ~Department() {
        std::cout << "Department析构函数: " << m_name << std::endl;
        delete[] m_name;
        delete m_manager;  // 释放经理对象
    }
    
    void printInfo() const {
        std::cout << "部门: " << m_name << ", 经理: ";
        m_manager->printInfo();
    }
};

void demonstrateComplexDeepCopy() {
    std::cout << "=== 复杂对象深度拷贝演示 ===" << std::endl;
    
    // 创建原始对象
    Person manager1("张三", 35);
    Department dept1("技术部", manager1);
    
    std::cout << "\n--- 拷贝部门对象 ---" << std::endl;
    // 深度拷贝:会递归拷贝所有成员,包括指向的对象
    Department dept2 = dept1;
    
    std::cout << "\n--- 修改原始部门信息 ---" << std::endl;
    // 修改原始部门的经理
    Person newManager("李四", 40);
    Department dept3("销售部", newManager);
    dept1 = dept3;  // 拷贝赋值
    
    std::cout << "\n--- 显示各部门信息 ---" << std::endl;
    dept1.printInfo();
    dept2.printInfo();  // dept2不受dept1修改的影响
    dept3.printInfo();
    
    std::cout << "\n--- 离开函数作用域 ---" << std::endl;
    // 所有对象都会正确释放自己的资源
}

int main() {
    demonstrateComplexDeepCopy();
    std::cout << "\n=== 程序结束 ===" << std::endl;
    return 0;
}

3. 现代C++的深度拷贝最佳实践

使用智能指针避免手动内存管理

cpp 复制代码
#include <iostream>
#include <memory>
#include <vector>
#include <cstring>

class ModernString {
private:
    std::unique_ptr<char[]> m_data;  // 使用智能指针自动管理内存
    int m_length;
    
public:
    // 构造函数
    ModernString(const char* str = "") : m_length(strlen(str)) {
        m_data = std::make_unique<char[]>(m_length + 1);
        strcpy(m_data.get(), str);
        std::cout << "ModernString构造函数: " << m_data.get() << std::endl;
    }
    
    // 拷贝构造函数(深度拷贝)
    ModernString(const ModernString& other) : m_length(other.m_length) {
        m_data = std::make_unique<char[]>(m_length + 1);
        strcpy(m_data.get(), other.m_data.get());
        std::cout << "ModernString拷贝构造函数: " << m_data.get() << std::endl;
    }
    
    // 拷贝赋值运算符(深度拷贝)
    ModernString& operator=(const ModernString& other) {
        if (this != &other) {
            m_length = other.m_length;
            m_data = std::make_unique<char[]>(m_length + 1);
            strcpy(m_data.get(), other.m_data.get());
        }
        std::cout << "ModernString拷贝赋值: " << m_data.get() << std::endl;
        return *this;
    }
    
    // 移动构造函数(C++11)
    ModernString(ModernString&& other) noexcept 
        : m_data(std::move(other.m_data)), m_length(other.m_length) {
        other.m_length = 0;
        std::cout << "ModernString移动构造函数" << std::endl;
    }
    
    // 移动赋值运算符(C++11)
    ModernString& operator=(ModernString&& other) noexcept {
        if (this != &other) {
            m_data = std::move(other.m_data);
            m_length = other.m_length;
            other.m_length = 0;
        }
        std::cout << "ModernString移动赋值" << std::endl;
        return *this;
    }
    
    // 注意:不需要显式定义析构函数!
    // 智能指针会自动管理内存释放
    
    const char* get() const {
        return m_data.get();
    }
    
    void printInfo(const char* name) const {
        std::cout << name << ": " << m_data.get() << std::endl;
    }
};

void demonstrateModernDeepCopy() {
    std::cout << "=== 现代C++深度拷贝演示 ===" << std::endl;
    
    ModernString str1("Hello Modern C++");
    ModernString str2 = str1;  // 深度拷贝
    ModernString str3;
    str3 = str1;  // 深度拷贝赋值
    
    str1.printInfo("str1");
    str2.printInfo("str2");
    str3.printInfo("str3");
    
    // 使用移动语义(避免不必要的拷贝)
    std::cout << "\n--- 移动语义演示 ---" << std::endl;
    ModernString str4 = std::move(str1);  // 移动构造
    str4.printInfo("str4");
    // str1现在处于有效但未指定状态
    
    std::cout << "\n--- 离开作用域,自动释放资源 ---" << std::endl;
}

int main() {
    demonstrateModernDeepCopy();
    return 0;
}

深度拷贝的关键要点总结

  1. 何时需要深度拷贝

    • 类包含原始指针成员

    • 指针指向动态分配的内存

    • 需要对象的完全独立性

  2. 必须实现的函数(Rule of Three):

    • 拷贝构造函数

    • 拷贝赋值运算符

    • 析构函数

  3. 现代C++改进(Rule of Five):

    • 增加移动构造函数

    • 增加移动赋值运算符

    • 使用智能指针简化内存管理

  4. 深度拷贝的步骤

    • 分配新内存

    • 复制数据内容

    • 在赋值运算符中处理自赋值情况

    • 在析构函数中正确释放资源

通过深度拷贝,可以确保对象的完全独立性,避免悬空指针、内存泄漏和重复释放等问题。

相关推荐
CHANG_THE_WORLD3 小时前
switch语句在汇编层面的几种优化方式 ,为什么能进行优化
汇编·算法·switch·汇编分析·switch case·switch case 汇编·switch case 语句
勇闯逆流河3 小时前
【C++】用红黑树封装map与set
java·开发语言·数据结构·c++
实心儿儿3 小时前
C++——内存管理
c++
Blossom.1184 小时前
把AI“撒”进农田:基于极值量化与状态机的1KB边缘灌溉决策树
人工智能·python·深度学习·算法·目标检测·决策树·机器学习
future_studio4 小时前
聊聊 Unity(小白专享、C# 小程序 之 加密存储)
开发语言·小程序·c#
一只鱼^_4 小时前
第 167 场双周赛 / 第 471 场周赛
数据结构·b树·算法·leetcode·深度优先·近邻算法·迭代加深
我狸才不是赔钱货4 小时前
容器:软件世界的标准集装箱
linux·运维·c++·docker·容器
云知谷4 小时前
【嵌入式基本功】单片机嵌入式学习路线
linux·c语言·c++·单片机·嵌入式硬件
m0_736927044 小时前
Spring Boot自动配置与“约定大于配置“机制详解
java·开发语言·后端·spring