为何拷贝构造函数执行后原对象指针成员值出现异常?
#include <iostream> #include <string.h> #include "Date.h" #include "Employee.h" using std::cout; using std::endl; using std::to_string; class TestOps { public: int sex = 1; string toString() { return " sex:" + to_string(sex) ; } }; class Test { public: TestOps* testOps; Test(const Test& t) :Test{} { this->testOps = new TestOps{ *(t.testOps) }; }; Test() { TestOps ops; //this->testOps = new TestOps{}; // it will be ok with this way this->testOps = &ops; } }; int main() { // code not understand Test t1; cout <<"first testOps:" << t1.testOps->toString() << endl; // sex: 1 Test t2{ t1 }; cout << "first testOps:" << t1.testOps->toString() << endl; // sex: -858893460 ???? why? cout << "second testOps:" << t2.testOps->toString() << endl; // sex: -858893460 ???? why? return 0; }
问题原因解析
核心问题是悬垂指针(指向已销毁对象的无效指针)导致的未定义行为:
Test的默认构造函数里,TestOps ops是局部变量,它的生命周期只限于构造函数执行过程中。构造函数执行完毕后,这个局部变量会被销毁,它占用的内存会被系统回收或标记为可复用。但你把this->testOps指向了这个局部变量的地址,这就导致t1.testOps变成了悬垂指针——它指向的内存已经不属于任何有效的TestOps对象。- 第一次打印
t1.testOps->toString()时,那块内存还没被其他操作覆盖,所以碰巧保留了sex=1的初始值,这属于运气好的未定义行为,不是正常逻辑。 - 创建
t2时,拷贝构造函数先调用Test{}(默认构造函数),此时栈上会新建一个局部TestOps ops,这个新变量很可能刚好占用了之前t1.testOps指向的内存区域,把原来的值覆盖成了垃圾数据。 - 同时,拷贝构造函数里
new TestOps{ *(t.testOps) }是在拷贝一块已经失效的内存里的垃圾值,所以t2.testOps指向的新对象里的sex也是随机垃圾值(你看到的-858893460是未初始化内存的典型值)。
注释里的this->testOps = new TestOps{};是正确写法,因为new出来的对象在堆上,生命周期不受构造函数限制(直到手动delete),不会产生悬垂指针。
内容的提问来源于stack exchange,提问作者xianshenglu
相关产品推荐
相关产品推荐

