大二学生血泪教训:C++深浅拷贝详解 面试必问

作为C++初学者,当你开始使用指针和动态内存分配时,“深拷贝”和“浅拷贝”是必须跨越的一道坎。不理解它们,轻则程序崩溃,重则内存泄漏。让我带你彻底搞懂这两个概念的区别、应用场景以及如何正确实现它们。

概述

浅拷贝因为初始化或者赋值一个对象时,会把指针也复制过去从而导致析构函数重复delete,导致世界末日

通过深拷贝用不同的内存空间,这样就不会重复delete掉两次了

一、什么是拷贝

在C++中,对象拷贝主要发生在两种场景:

1.使用一个对象初始化另一个对象,说人话就是新建一个对象等于另一个对象

Myclass obj2 = obj1 (这里调用了拷贝构造函数)

2.将一个对象赋值给另一个已存在的对象,也就是令一个对象和另一个对象相等

obj2 = obj1 (这里调用了赋值运算符 operator= )

默认情况下,编译器会自动生成拷贝构造函数和赋值运算符。

它们逐个成员地复制对象的内容。对于基本数据类型(int, float, char等),这种复制是直接的值拷贝,这一部分是没有问题的。问题就出在当我们的类成员包含指针,并且这些指针指向动态分配的内存(堆内存)时。

二、浅拷贝潜伏危机

浅拷贝只复制对象中的内存地址,而不复制这个指针所指的那块内存。结果是,拷贝后的对象和原对象的指针成员指向同一块堆内存

刚才我们提到的默认情况下, 就是编译器的浅拷贝。

我们来看一下运行结果:

构造函数调用: 分配内存 @0x7ffee3d05700

对象地址: 0x7ffee3d056f0, data指向: 0x7ffee3d05700

对象地址: 0x7ffee3d056e0, data指向: 0x7ffee3d05700 // obj2.data 和 obj1.data 指向同一个地址!

析构函数调用: 释放内存 @0x7ffee3d05700 // obj2 释放了内存

析构函数调用: 释放内存 @0x7ffee3d05700 // obj1 试图再次释放同一块内存 -> 崩溃!!!

其中的问题所在就是,obj1和obj2中的data指向了同一个堆内存空间,这就导致在delete中重复删除了两次

当时我在这里有一个疑问,就是前面加了if 该地址 != nullptr 再释放为什么不可以解决呢

在delete掉第一个obj中的data时,该内存确实被释放了,但是obj2中data仍然存放了 0x7ffee3d05700,所以判断不为nullptr,所以就会再delete一次这个内存空间

三、深拷贝实现安全保障

深拷贝可以给指针成员重新分配堆内存,并将原对象指针指向的内容(不仅仅是地址)复制到新分配的内存中。这样,拷贝后的对象和原对象拥有完全独立的数据副本,不会互相影响

那我们什么时候需要深拷贝呢?

1.类成员包含指向堆内存的原始指针

2.类管理着需要独占性控制的资源(如文件句柄、网络连接等)

什么时候可以浅拷贝呢?

1.类成员只包含基本数据类型 (`int`, `float`, `char` 等)。

2.成员包含本身能正确管理拷贝的类对象 (例如 std::string, std::vector)。成员包含本身能正确管理拷贝的类对象 (例如 `std::string`, `std::vector`)

3.设计本身就是需要共享资源

四、现代C++可以避免手动深拷贝

1.使用标准库容器 (std::vector, std::string 等)

2.使用智能指针 (std::unique_ptr, std::shared_ptr)

#内存管理##深浅拷贝##大二[话题]##C++#
全部评论

相关推荐

点赞 评论 收藏
分享
8.25  全程1h多  各个方面都问了一些:1.目前实习最大的收获是开始根据实习内容问相关问题,并根据回答中提到的进一步提问:2.介绍iic和spi的区别,两者的最高速率是?3.确认实习内容做的是哪个层面的工作,是偏向驱动开发还是协议解析4.用户态和内核态的区别是5.iic总线最多能挂载多少个设备6.iic的起始信号和结束信号是怎么样的7.那spi的起始信号和结束信号又是怎么样的8.有实际测过iic和spi接口的信号波形9. 多从机的情况下spi的片选如何设计10.讲一下Linux系统的中断机制,追问底层实现逻辑11.中断有什么注意点吗12.中断和轮询哪个效率高,选择其一的考虑因素有13.Linux系统的驱动有哪几种分类14.介绍一下字符设备驱动问下一段实习内容:15.详细介绍其中一个需求开发具体如何实现的问在校项目细节:16. 其中激光雷达和板卡的通信方式17. 还问了项目中视觉识别的细节问题问团队协作:18. 团队成员之间意见冲突时如何处理19. 有同事指出你的问题时你会怎么办问八股:20. c语言的编译过程21. 引用和指针的区别22. 介绍volitate23.介绍c++的纯虚数24.进程和线程的区别25.进程的通信方式26.信号量和互斥锁的应用场景27.堆栈的区别28.在c语言中如何判断两浮点数相等29.指针数组和数组指针的区别30.什么是平衡二叉树31.介绍排序算法手撕:32.反转字符串中的单词33.说完思路后追问一个单词要反转几次其他:34.问理想的工作环境35.你认为比较合理的上班时间36.职业规划37.工作地倾向38.手上offer情况反问
发面经攒人品
点赞 评论 收藏
分享
评论
3
6
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务