大二学生血泪教训: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++#
全部评论

相关推荐

评论
2
4
分享

创作者周榜

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