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

C++向vector添加带动态分配属性的对象触发double free错误问题

问题根本原因

第一个代码报错的核心原因

  • 你的Matrix类违反了C++的三五法则:当你自定义了析构函数来释放堆内存时,必须同步自定义拷贝构造函数、拷贝赋值运算符,或者移动构造函数、移动赋值运算符,否则编译器生成的默认版本只会做浅拷贝。
  • std::vector在容量不足需要扩容时,会申请更大的内存空间,将原有元素复制/移动到新空间后,销毁旧空间里的所有元素。
  • 结合你的运行日志完整流程如下:
    1. 第一次emplace_back(1),vector容量从0扩到1,构造第一个Matrix对象,地址0x5642c4882eb0,data指向堆内存A
    2. 第二次emplace_back(2),vector容量从1扩到2,编译器生成的默认拷贝构造函数把旧地址的0x5642c4882eb0对象浅拷贝到新空间,新对象的data也指向堆内存A
    3. 旧空间的0x5642c4882eb0对象被析构,释放了堆内存A
    4. 程序结束时新空间里的对象生命周期结束,析构函数会再次释放已经被释放的堆内存A,触发double free错误。

指针版本的问题

  • 指针版本不会出现double free的原因是:vector存储的只是指针本身,扩容的时候只拷贝指针值,不会触发Matrix对象的拷贝/析构,所有Matrix对象的内存都是你手动new和delete的,不会出现重复释放的问题。
  • 你当前写的指针版本不存在内存泄漏:循环里new了2个对象,后续循环delete了2个对象,所有堆内存都被正确回收。但这种写法的风险点在于:如果中间逻辑抛出异常,很容易出现delete没执行的情况,导致内存泄漏,而且手动管理内存确实繁琐。
最优修复方案

不需要用指针,你只要给Matrix类补充移动构造函数即可,满足vector扩容时的移动语义,避免浅拷贝:

struct Matrix {
    int * data = nullptr;
    int sz = 0;

    Matrix(int sz) : sz(sz) {
        std::cout << this << " constructed" << std::endl;
        data = new int[sz];
        for (int i = 0; i < sz; i++) {
            data[i] = sz;
        }
    }
    // 补充移动构造函数
    Matrix(Matrix&& other) noexcept {
        std::cout << this << " moved from " << &other << std::endl;
        data = other.data;
        sz = other.sz;
        // 把原对象的指针置空,避免析构时释放共享的内存
        other.data = nullptr;
        other.sz = 0;
    }
    // 可选:禁止拷贝构造和拷贝赋值,避免误操作出现浅拷贝
    Matrix(const Matrix&) = delete;
    Matrix& operator=(const Matrix&) = delete;

    ~Matrix() {
        std::cout << this << " destructed" << std::endl;
        // 空指针delete是安全的
        delete[] data;
    }
};

补充移动构造后,vector扩容时会直接把旧对象的内存所有权转移到新对象,旧对象析构时只会释放空指针,不会出现double free的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 23:30:02