You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何拷贝构造函数执行后原对象指针成员值出现异常?

#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 09:50:35