3. 默认拷贝构造函数:“我要给你一模一样的副本!” 有时候,我们希望“复制”一个对象。比如你有一个对象 obj1,要把它赋值给另一个对象 obj2。这时候就需要用到拷贝构造函数。如果你没有自己写,C++ 会自动给你生成一个默认的拷贝构造函数。它会把原对象的成员变量逐个复制过来。 class MyClass {
public:
int value;
MyClass(int val) : value(val) {}
MyClass(const MyClass& other) : value(other.value) { // 默认拷贝构造函数
}
};
int main() {
MyClass obj1(10);
MyClass obj2 = obj1; // 或者这样写 MyClass obj2(obj1); 默认拷贝构造函数被调用
std::cout << obj1.value << " " << obj2.value << std::endl; // 输出 10 10
}
运行结果会显示:
10 10 这段代码中,obj2 会有和 obj1 一样的值,因为默认拷贝构造函数进行了“浅拷贝”,逐个成员地复制了数据。这对于像整数这类简单类型完全没问题,两个对象会独立存在。 指针的“浅拷贝”但是,如果类中有指针成员,默认拷贝构造函数就会有问题。因为它只是简单地复制指针的值(即内存地址),导致两个对象指向同一块内存。这样,当一个对象销毁时,另一对象仍然会持有这个内存地址,从而可能引发“双重释放”的问题。 假设你的类有一个指针成员,像这样: class MyClass {
public:
int* ptr;
MyClass(int val) {
ptr = new int(val); // 动态分配内存
}
MyClass(const MyClass& other) : ptr(other.ptr) { // 默认拷贝构造函数
}
~MyClass() { // 析构函数
delete ptr; // 释放内存
}
};
int main(){
MyClass obj1(10);
MyClass obj2(obj1);
}
说明: 在这个例子中,我们有一个类 MyClass,里面有一个指针 ptr,指向动态分配的内存。在 main 函数里,我们创建了 obj1 对象,并通过拷贝构造函数创建了 obj2 对象。 默认的拷贝构造函数 MyClass(const MyClass& other) 是做“浅拷贝”的,它只是简单地将 other.ptr 的值(即内存地址)赋给 ptr。这就意味着,obj1 和 obj2 都指向同一块内存。 问题来了: - 现在,obj1 和 obj2 都有指向同一块内存的指针。
- 当 obj1 和 obj2 被销毁时,它们的析构函数会分别调用 delete ptr; 来释放 ptr 指向的内存。
- 结果就会出现“双重释放”的问题:obj1 销毁时会释放 ptr 指向的内存,然后 obj2 销毁时再试图释放同一块内存。这会导致程序崩溃,因为你不能两次释放同一块内存。
解决办法:为了避免这个问题,通常我们会实现一个“深拷贝”的拷贝构造函数,确保每个对象有自己独立的内存,而不是共享同一个内存区域。 MyClass(const MyClass& other) {
ptr = new int(*(other.ptr)); // 为每个对象分配不同的内存
}
这样,obj1 和 obj2 就会有各自独立的 ptr,指向不同的内存空间,避免了“双重释放”问题。 深拷贝、浅拷贝不熟悉的朋友可以看这篇文章 :震惊!80%的程序员都搞不懂深拷贝和浅拷贝的区别!
|