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

C++类对象赋值时析构函数调用及double free问题咨询

问题核心原因

你自定义的类仅实现了构造函数、析构函数和浅拷贝的拷贝构造函数,未自定义拷贝赋值运算符,C++编译器会自动生成默认的逐位拷贝版本的赋值运算符,直接复制指针本身而非指针指向的资源,最终导致浅拷贝、资源泄漏、重复释放的问题,完全符合三五法则的适用场景(类自定义了析构函数就需要配套实现拷贝/移动相关的特殊成员函数)。

逐行执行流程对应你的输出

你观察到的现象和输出完全匹配如下执行逻辑:

  1. 执行TestClass foo;:调用默认构造函数,输出Creating,foo的some_data指针指向第一块申请的堆内存,随后输出Created once。
  2. 执行foo = TestClass();:
    • 首先生成一个临时的TestClass匿名对象,调用默认构造函数,输出第二个Creating,临时对象的some_data指向第二块申请的堆内存。
    • 调用编译器生成的默认拷贝赋值运算符,直接将临时对象的some_data指针值复制给foo,foo原本持有的第一块内存的指针被直接覆盖,没有被释放,这就是你观察到的「原对象资源从未被释放」的原因。
    • 赋值操作完成后,临时对象的生命周期结束,触发析构函数,输出第一个Deconstructing,将临时对象持有的第二块内存释放。此时foo的some_data已经指向这块被释放的内存,成为野指针。
  3. 执行到输出Created twice后,main函数执行结束,foo的生命周期结束,触发析构函数,输出第二个Deconstructing,尝试释放已经被释放的第二块内存,触发double free报错,程序崩溃。
修复方案

方案1:实现深拷贝语义(需要对象可拷贝时使用)

补充自定义拷贝赋值运算符,先释放自身原有资源,再深拷贝目标对象的资源,同时修改原有浅拷贝的拷贝构造函数为深拷贝实现:

// 修改拷贝构造为深拷贝
TestClass(const TestClass& t) {
    std::cout << "Copy" << std::endl;
    some_data = (int*)malloc(10*sizeof(int));
    memcpy(some_data, t.some_data, 10*sizeof(int));
}

// 新增拷贝赋值运算符
TestClass& operator=(const TestClass& t) {
    if (this == &t) return *this; // 处理自赋值场景
    // 先释放自身原有资源
    free(some_data);
    std::cout << "Copy assign" << std::endl;
    // 深拷贝目标对象的资源
    some_data = (int*)malloc(10*sizeof(int));
    memcpy(some_data, t.some_data, 10*sizeof(int));
    return *this;
}

修改后赋值操作会先释放foo原有的第一块内存,再拷贝临时对象的资源,临时对象析构释放的是自己的资源,foo持有的是独立的新资源,程序结束时释放不会触发重复释放。

方案2:禁用拷贝语义(不需要对象可拷贝时使用)

如果你的类不需要支持拷贝操作,可以直接禁用拷贝构造和拷贝赋值,编译期就会阻止非法的赋值操作,避免运行时错误:

TestClass(const TestClass&) = delete;
TestClass& operator=(const TestClass&) = delete;

内容的提问来源于stack exchange,提问作者Matthew

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 16:27:03