news 2026/10/7 16:24:29

深拷贝、浅拷贝

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深拷贝、浅拷贝

一、先理解核心概念:拷贝的本质

拷贝的目的是创建一个新对象,使其与原对象的“内容”一致。而深浅拷贝的核心差异,在于对「堆内存资源」的处理方式:

  • 栈内存(如int、char、指针变量本身):所有拷贝都会复制,无深浅之分;
  • 堆内存(如new出来的数组/对象):浅拷贝只复制“指向堆内存的指针”,深拷贝会复制“指针指向的堆内存内容”。

二、浅拷贝(Shallow Copy):默认的“表层拷贝”

1. 定义

浅拷贝是C++编译器默认生成的拷贝行为:仅复制对象的「栈上成员」(包括指针变量的地址值),不复制指针指向的「堆内存资源」。新对象和原对象共享同一块堆内存,相当于“多个管家管同一个房子”。

2. 浅拷贝的问题:资源冲突(必现!)

如果对象包含堆内存资源,浅拷贝会导致两个致命问题:

  • 重复释放:两个对象析构时,都会释放同一块堆内存,触发未定义行为(程序崩溃);
  • 数据篡改:修改新对象的堆数据,原对象的堆数据也会被改(共享资源)。
3. 示例:浅拷贝的崩溃演示

我们用一个「管理动态数组」的类,演示浅拷贝的问题:

#include<iostream>#include<cstring>usingnamespacestd;// 管理动态字符数组的类(含堆内存资源)classStringWrapper{private:char*str;// 指针指向堆内存的字符数组public:// 构造函数:分配堆内存,初始化字符串StringWrapper(constchar*s){cout<<"构造:分配堆内存"<<endl;str=newchar[strlen(s)+1];// 堆内存分配strcpy(str,s);}// 析构函数:释放堆内存~StringWrapper(){cout<<"析构:释放堆内存"<<endl;if(str){delete[]str;// 释放堆数组str=nullptr;}}// 打印字符串(方便测试)voidprint(){cout<<str<<endl;}// 手动修改堆数据(演示共享问题)voidset(constchar*s){strcpy(str,s);}// 【注意】编译器默认生成的浅拷贝构造/赋值运算符:// StringWrapper(const StringWrapper& other) = default;// StringWrapper& operator=(const StringWrapper& other) = default;};intmain(){// 原对象:分配堆内存,存储"hello"StringWrappers1("hello");// 浅拷贝:s2的str指针和s1的str指向同一块堆内存StringWrapper s2=s1;// 问题1:修改s2的堆数据,s1也被篡改(共享资源)s2.set("world");cout<<"s1的值:";s1.print();// 输出world(而非hello)// 问题2:函数结束,s2先析构→释放堆内存;s1析构→重复释放同一块内存→崩溃return0;}
运行结果(崩溃+错误):
构造:分配堆内存 s1的值:world 析构:释放堆内存 析构:释放堆内存 // 随后程序崩溃(double free or corruption,重复释放内存)

三、深拷贝(Deep Copy):独立的“完整拷贝”

1. 定义

深拷贝需要手动实现:不仅复制对象的栈上成员,还会为新对象重新分配一块独立的堆内存,并将原对象堆内存中的内容完整复制过去。新对象和原对象拥有完全独立的堆资源,相当于“复制房子的所有内容,盖一栋新的一模一样的房子”。

2. 深拷贝的实现:遵循“三法则/五法则”

要实现深拷贝,必须手动编写:

  • 拷贝构造函数(创建新对象时);
  • 拷贝赋值运算符(已有对象赋值时);
  • 析构函数(释放堆资源,已有)。
3. 示例:实现深拷贝解决问题

基于上面的类,补充深拷贝的实现(关键修改处标注):

#include<iostream>#include<cstring>usingnamespacestd;classStringWrapper{private:char*str;public:// 构造函数StringWrapper(constchar*s){cout<<"构造:分配堆内存"<<endl;str=newchar[strlen(s)+1];strcpy(str,s);}// 【核心1】深拷贝构造函数StringWrapper(constStringWrapper&other){cout<<"深拷贝构造:分配新堆内存"<<endl;// 步骤1:为新对象分配独立的堆内存str=newchar[strlen(other.str)+1];// 步骤2:复制原对象堆内存的内容(而非指针)strcpy(str,other.str);}// 【核心2】深拷贝赋值运算符(遵循“先释放、再分配、最后复制”)StringWrapper&operator=(constStringWrapper&other){cout<<"深拷贝赋值:释放旧内存,分配新内存"<<endl;// 步骤0:防止自赋值(如s1 = s1)if(this==&other)return*this;// 步骤1:释放当前对象的旧堆内存delete[]str;// 步骤2:分配新堆内存str=newchar[strlen(other.str)+1];// 步骤3:复制原对象的堆内容strcpy(str,other.str);return*this;}// 析构函数~StringWrapper(){cout<<"析构:释放堆内存"<<endl;if(str){delete[]str;str=nullptr;}}voidprint(){cout<<str<<endl;}voidset(constchar*s){strcpy(str,s);}};intmain(){StringWrappers1("hello");// 深拷贝构造:s2有独立的堆内存StringWrapper s2=s1;// 修改s2的堆数据,s1不受影响(资源独立)s2.set("world");cout<<"s1的值:";s1.print();// 输出hellocout<<"s2的值:";s2.print();// 输出world// 赋值运算符测试:深拷贝StringWrappers3("test");s3=s1;// 调用深拷贝赋值cout<<"s3的值:";s3.print();// 输出hello// 析构:三个对象释放各自的堆内存,无冲突return0;}
运行结果(安全无崩溃):
构造:分配堆内存 深拷贝构造:分配新堆内存 s1的值:hello s2的值:world 构造:分配堆内存 深拷贝赋值:释放旧内存,分配新内存 s3的值:hello 析构:释放堆内存 析构:释放堆内存 析构:释放堆内存

四、深拷贝 vs 浅拷贝:核心区别对比

特性浅拷贝深拷贝
拷贝内容仅复制栈上成员(如指针地址)复制栈上成员 + 重新分配堆内存并复制内容
资源归属新对象与原对象共享堆资源新对象与原对象拥有独立堆资源
安全性有堆资源时必出问题(重复释放/篡改)安全,无资源冲突
效率高(仅复制表层数据)稍低(需分配堆内存+复制内容)
实现方式编译器默认生成(=default)手动实现拷贝构造+赋值运算符
适用对象无堆资源的纯栈对象(如Point{x,y})含堆资源的对象(如动态数组、自定义指针)

五、适用场景:什么时候用深/浅拷贝?

1. 浅拷贝的适用场景

只有当对象无堆内存资源时,才适合用浅拷贝(编译器默认生成即可):

  • 纯栈对象:如struct Point { int x; int y; }、class Student { string name; int age; }(string内部已实现深拷贝,对外部是浅拷贝);
  • 明确需要共享资源的场景(需配合引用计数):如std::shared_ptr(浅拷贝指针,但通过引用计数避免重复释放)。
2. 深拷贝的适用场景

只要对象包含堆内存资源(自定义指针、动态数组、手动new的对象),必须实现深拷贝:

  • 自定义容器类(如自定义栈、队列,内部有动态数组);
  • 管理系统资源的类(如文件句柄、网络连接的封装类,需独立管理资源);
  • 要求“对象独立”的场景(修改新对象不影响原对象)。

六、补充:C++11后的优化——移动语义(替代深拷贝)

深拷贝的效率问题(分配内存+复制内容),在处理临时对象(如函数返回值)时尤为明显。C++11引入的移动语义(Move Semantics)可以替代深拷贝:

  • 移动构造/赋值:直接“偷走”临时对象的堆资源(转移指针所有权),无需分配新内存,效率和浅拷贝一样高;
  • 实现:编写StringWrapper(StringWrapper&& other)(移动构造)和operator=(StringWrapper&& other)(移动赋值),用std::move转移资源。

示例(简化的移动构造):

// 移动构造函数:转移堆资源,而非复制StringWrapper(StringWrapper&&other)noexcept{cout<<"移动构造:转移堆资源"<<endl;str=other.str;// 直接拿走指针other.str=nullptr;// 原对象放弃资源所有权,避免析构释放}

七、总结(关键点回顾)

  1. 核心区别:深浅拷贝的本质是「是否复制堆内存资源」——浅拷贝复制指针地址(共享资源),深拷贝复制指针指向的内容(独立资源);
  2. 浅拷贝:编译器默认生成,仅适用于无堆资源的纯栈对象,有堆资源时必须禁用(=delete);
  3. 深拷贝:手动实现拷贝构造+赋值运算符,适用于含堆资源的对象,保证资源独立、安全;
  4. 优化:临时对象场景用移动语义替代深拷贝,兼顾安全和效率。

简单来说:只要你的类里有new/malloc,就必须手动实现深拷贝;如果没有,用编译器默认的浅拷贝就够了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 10:02:12

RK3576-Android15原生相机Camera2 修改USB相机预览和成像方向

提示&#xff1a;RK3576-Android15 谷歌原生相机Camera2 使用客户定制的USB相机&#xff0c;发现预览和成像方向错位&#xff0c;相机软件需要适配USB相机 文章目录前言-需求一、基础-参考资料-思路参考资料基础补充架构图了解Camera相关专栏零散知识了解部分相机源码参考&…

作者头像 李华
网站建设 2026/10/6 19:47:33

ODA X9-2 双归档路径配置

周五下午客户突然反馈有套ODA X9-2的orcl实例报归档满了&#xff0c;目前没运维&#xff0c;需要帮忙处理一下问题&#xff0c;登录环境后发现db_recovery_file_dest_size配置了300G&#xff0c;当前使用率100%了救急情况 先把db_recovery_file_dest_size 扩展到400G&#xff0…

作者头像 李华
网站建设 2026/10/7 4:52:54

AI agents协作分析卫星图像:评估公司实际经营状况

AI agents协作分析卫星图像:评估公司实际经营状况 关键词:AI agents、卫星图像分析、公司经营状况评估、多智能体协作、遥感技术 摘要:本文聚焦于利用AI agents协作分析卫星图像以评估公司实际经营状况这一前沿技术。首先介绍了该技术的背景,包括目的、预期读者等。接着阐述…

作者头像 李华
网站建设 2026/10/6 17:39:07

新闻稿件自动生成上线:媒体行业的生产力变革

新闻稿件自动生成上线&#xff1a;媒体行业的生产力变革 在信息爆炸的时代&#xff0c;一条突发新闻从发生到传播的“黄金窗口”可能只有几分钟。当某地发生地震、股市异动或重大政策发布时&#xff0c;谁能在最短时间内产出准确、清晰的报道&#xff0c;谁就掌握了话语权。传…

作者头像 李华
网站建设 2026/10/6 12:04:52

AI绘画提示词优化器上线:创意工作者的新工具

AI绘画提示词优化器上线&#xff1a;创意工作者的新工具 在设计师等待AI生成一张图像的几秒钟里&#xff0c;灵感可能已经溜走。如今&#xff0c;越来越多的内容创作者发现&#xff0c;他们不是在“使用”AI绘画工具&#xff0c;而是在“忍受”它——输入提示词、点击生成、盯着…

作者头像 李华
网站建设 2026/10/6 13:41:02

AI原生应用中的文化敏感性:全球伦理考量

AI原生应用中的文化敏感性:全球伦理考量 关键词:AI原生应用、文化敏感性、全球伦理、跨文化交流、伦理准则、文化差异、AI伦理 摘要:本文深入探讨AI原生应用中的文化敏感性与全球伦理考量。首先介绍其主题背景,强调在全球化时代,AI应用广泛渗透各领域,文化敏感性和全球…

作者头像 李华