在 C++ 中,当我们涉及到类对象的拷贝操作时,深拷贝和浅拷贝是两个绕不开的重要概念。它们在处理对象数据成员的复制方式上有着显著的差异,并且这种差异会对程序的运行和内存的管理产生深远的影响。
int
、double
等),这种复制方式能够完整地复制数据。比如以下代码:
#include
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
的析构函数被调用并尝试再次删除这块已经被释放的内存时,就会导致程序出现未定义行为,很可能引发运行时错误(如段错误等)。
所以涉及到指针操作的时候,我们就可以考虑深拷贝。
#include
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
分配的内存)、文件句柄、网络连接等需要进行资源管理的对象时,就需要谨慎考虑拷贝方式。.以上就是关于深拷贝和浅拷贝的简单阐述,如有不当还请多多指正。.