在 C++ 中,当我们涉及到类对象的拷贝操作时,深拷贝和浅拷贝是两个绕不开的重要概念。它们在处理对象数据成员的复制方式上有着显著的差异,并且这种差异会对程序的运行和内存的管理产生深远的影响。
1、什么是浅拷贝
- 浅拷贝是指在进行对象拷贝时,只是简单地将源对象中的成员变量的值逐个复制到目标对象中。对于普通的数据类型成员(如
int
、double
等),这种复制方式能够完整地复制数据。 - 但当源对象包含指针成员时,浅拷贝仅仅复制了指针的值,而不是指针所指向的数据,这就导致新创建的目标对象中的指针和源对象中的指针指向同一块内存空间。
比如以下代码:
#include<iostream>
using namespace std;
class MyClass {
public:
MyClass(int vaule)
{
value =new int(vaule);
}//赋值构造
MyClass(const MyClass& other) {
value = other.value;
}//浅拷贝
~MyClass() {
delete value;
}//析构函数
int *value;
};
int main() {
MyClass obj1(20);
MyClass obj2(obj1);
cout << "obj1.value: " << *obj1.value << endl;
cout << "obj2.value: " << *obj2.value << endl;
//更改vaule大小
*(obj2.value) = 30;
cout << "obj1.value: " << *obj1.value << endl;
cout << "obj2.value: " << *obj2.value << endl;
return 0;
}
代码写完后,编译器并没有检测出错误,但是会抛出异常处理。运行结果如下:
为什么只是更改了obj2的value的大小,obj1的value结果也一起改变了?就是因为浅拷贝问题使得两个指针对象指向了同一块内存。
接着我们谈论一下为什么编译器在运行时候会抛出异常处理:
这就是程序抛出的异常,这是在堆区内存delete时候出现了问题。
当其中一个对象(比如假设先析构 obj1
)的析构函数被调用并通过 delete value;
释放了这块内存后,另一个对象(obj2
)的 value
指针就变成了悬空指针。
随后当 obj2
的析构函数被调用并尝试再次删除这块已经被释放的内存时,就会导致程序出现未定义行为,很可能引发运行时错误(如段错误等)。
所以涉及到指针操作的时候,我们就可以考虑深拷贝。
2、什么是深拷贝
- 深拷贝在进行对象拷贝时,对于源对象中包含的指针成员,会重新分配一块新的内存空间,并将原指针所指向的数据完整地复制到新分配的内存空间中,然后将新创建的目标对象中的指针指向新分配的这块内存。
- 这样,新对象和原对象在包含指针成员的情况下,各自拥有独立的内存空间,相互之间不会因为对其中一个对象的操作而影响到另一个对象。
- 比如以下代码:
-
#include<iostream> using namespace std; class MyClass { public: MyClass(int vaule) { value =new int(vaule); } MyClass(const MyClass& other) { value = new int; *value = *other.value; } ~MyClass() { delete value; } int *value; }; int main() { MyClass obj1(20); MyClass obj2(obj1); cout << "obj1.value: " << *obj1.value << endl; cout << "obj2.value: " << *obj2.value << endl; *(obj2.value) = 30; cout << "obj1.value: " << *obj1.value << endl; cout << "obj2.value: " << *obj2.value << endl; return 0; }
这段代码和之前那段浅拷贝代码大致是一样的,只不过在拷贝构造函数改了一点内容:
-
value = new int; *value = *other.value;
这和之前的浅拷贝不同,他为新对象在堆区建立了一块新的内存区域,而不是像浅拷贝一样新旧对象共享一个内存区。
-
程序运行结果如下:
-
-
可见仅仅改变obj2的value不会影响obj1的value值。并且程序运行结束后,对象生命周期结束时,调用析构函数就不会出现浅拷贝那样指针悬空的问题了,而是单独的析构两块不同的内存区域。
-
结语:
- 如果类中的成员变量都是简单数据类型(如
int
、double
、char
等),没有指针或其他资源管理类型的成员,那么浅拷贝通常就足够满足需求。因为对于这些简单数据类型,浅拷贝能够准确地复制每个成员的值,不会出现诸如内存管理不当或数据一致性方面的问题。 - 当类中包含指针成员,且指针指向的是动态分配的内存(如通过
new
分配的内存)、文件句柄、网络连接等需要进行资源管理的对象时,就需要谨慎考虑拷贝方式。 - 如果希望新对象和原对象在涉及这些资源时相互独立,即对其中一个对象的操作(如释放资源、修改资源内容等)不会影响到另一个对象,那么应该选择深拷贝。深拷贝会为新对象重新分配相应的资源,并将原对象的资源内容完整复制过来,确保每个对象都能独立地管理自己的资源。
.以上就是关于深拷贝和浅拷贝的简单阐述,如有不当还请多多指正。😇😇😇.