如何为C++类中的两个指针成员编写深拷贝构造函数?
问题分析与解决方案
老哥,先给你揪出代码里的两个核心问题,再一步步实现正确的深拷贝构造函数:
1. 原构造函数的致命bug
你写的构造函数里藏着两个大坑:
number1 = new float; number2 = new float; number1 = &l; number2 = &w;
- 你先用
new float在堆上分配了内存,但转头就把指针指向了函数参数l和w的栈地址,这直接导致堆内存泄漏——new出来的那块内存再也找不到了,没法释放。 - 函数参数
l和w是栈上的临时变量,构造函数执行完就会被销毁,后续你再访问*number1或*number2,就是在访问已经失效的内存,轻则输出乱码,重则直接崩溃。
正确的构造函数应该把参数的值拷贝到你new出来的堆内存里,两种写法都可以:
abc(float l, float w) { // 写法1:new的时候直接初始化值 number1 = new float(l); number2 = new float(w); // 写法2:先分配内存再赋值 // number1 = new float; // *number1 = l; // number2 = new float; // *number2 = w; }
2. 实现真正的深拷贝构造函数
你当前的拷贝构造函数是浅拷贝,只是把指针的值复制了一遍:
abc(const abc& ob) { number1 = ob.number1; number2 = ob.number2; }
这会导致新对象和原对象的指针指向同一块堆内存,一旦其中一个对象被销毁(析构函数释放内存),另一个对象再访问这块内存就会出错;要是两个对象都析构,还会触发重复释放内存的崩溃。
深拷贝的核心就是:给新对象重新分配独立的堆内存,然后把原对象指针指向的值拷贝过去,而不是复制指针本身。
完整修正后的代码
别忘了加析构函数,用来释放堆内存,不然还是会泄漏:
#include <iostream> using namespace std; class abc { float *number1; float *number2; public: // 修正后的构造函数 abc(float l, float w) { number1 = new float(l); number2 = new float(w); } // 深拷贝构造函数 abc(const abc& ob) { // 为新对象分配独立堆内存,同时拷贝原对象的值 number1 = new float(*ob.number1); number2 = new float(*ob.number2); } // 析构函数:释放堆内存 ~abc() { delete number1; delete number2; } float get_1() { return *number1; } float get_2() { return *number2; } void display() { cout << "Number 1 : " << *number1 << endl; cout << "Number 2 : " << *number2 << endl; } // 可选但推荐:重载赋值运算符(遵循Rule of Three) abc& operator=(const abc& ob) { // 先防止自赋值,不然会把自己的内存释放了再拷贝 if (this == &ob) { return *this; } // 先释放当前对象的旧内存 delete number1; delete number2; // 重新分配内存并拷贝值 number1 = new float(*ob.number1); number2 = new float(*ob.number2); return *this; } }; // 测试一下效果 int main() { abc obj1(10.5f, 20.3f); abc obj2 = obj1; // 调用拷贝构造函数 obj1.display(); // 输出:Number 1 : 10.5 | Number 2 : 20.3 obj2.display(); // 和obj1输出一样,但内存是独立的 // 修改obj1的值,验证深拷贝 *obj1.number1 = 50.0f; // 这里为了测试直接访问私有成员,实际应该加setter方法 obj1.display(); // 输出:Number 1 : 50 | Number 2 : 20.3 obj2.display(); // 还是输出10.5和20.3,说明两个对象完全独立 return 0; }
额外提醒:Rule of Three原则
当你的类用到堆内存指针时,一定要遵循Rule of Three:如果你定义了拷贝构造函数、拷贝赋值运算符、析构函数中的任意一个,就必须把另外两个也实现了。这样能彻底避免内存泄漏、重复释放、悬垂指针这些常见问题。
内容的提问来源于stack exchange,提问作者john
相关产品推荐
相关产品推荐

