当前位置: 首页 > news >正文

深度拷贝详解

什么是深度拷贝?

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

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

1. 基本的深度拷贝实现

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

#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. 复杂对象的深度拷贝

包含嵌套对象的深度拷贝

#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++的深度拷贝最佳实践

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

#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. 深度拷贝的步骤

    • 分配新内存

    • 复制数据内容

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

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

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

http://www.dtcms.com/a/503095.html

相关文章:

  • 李宏毅机器学习笔记21-26周汇总
  • 特别分享:IOPaint概念及基础知识
  • 【微服务】(2) 环境和工程搭建
  • 做网站工具 不懂代码网站开发项目交接
  • 外贸网站推广平台哪个好网站建设公司相关资质
  • 2025年智能Agent终极指南:从概念到高效营销工具
  • 网络安全 | 深入了解 X.509 证书及其应用
  • Token快过期的三种续期方案
  • 网站建设html代码优化广东新闻联播今天
  • 微服务之配置中心Nacos
  • 好网站推荐娄底高端网站建设
  • h5游戏免费下载:保卫机器人
  • 如何解决 pip install -r requirements.txt 私有索引未设为 trusted-host 导致拒绝 问题
  • Redis(71)如何确保Redis分布式锁的可靠性?
  • docker安装php+apache
  • 数据查询网站包装设计公司排行榜
  • [JavaEE初阶]网络协议-状态码
  • 5.2 UDP (答案见原书 P230)
  • 做资讯类网站需要什么资质宁波seo推广公司电话
  • 第十五部分:信号量和读写锁
  • 无刷直流电机(BLDC)数学模型深度解析
  • 第七届全球校园人工智能算法精英大赛-算法巅峰赛产业命题赛第二赛季--最后一题解读
  • Spring Boot 3零基础教程,WEB 开发 内容协商源码分析 默认的 HttpMessageConverter 自定义返回值格式 笔记34
  • 【嵌入式面试题】STM32F103C8T6 完整元器件解析 + 面试问题答案
  • docker入门教程--部署nginx和tomcat
  • 3.1.2 从NoSQL到图数据库的转型
  • 【洛谷】哈希表实战:5 道经典算法题(unordered_map/set 应用 + 避坑指南)
  • 昆明 网站推广营销策划公司简介
  • 医院做网站是最简单的前端吗长沙水业网站是哪家公司做的
  • GridPlayer,一个好用的多视频同步播放器