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

为何此场景下Buf类的析构函数会被调用两次?

问题根源:缺少移动构造函数导致的浅拷贝

你的问题核心是Buf类没有定义移动构造函数和移动赋值运算符,编译器只能生成默认的拷贝构造——这会导致你以为的“所有权转移”变成了浅拷贝,最终同一个内存指针被两个Buf对象持有,触发两次析构。

具体过程拆解

  1. 在produce函数里,你创建了一个临时Buf对象Buf(new uint8_t[msg_size], 10),并以右值形式传给offer函数。
  2. 进入offer后,你调用push(std::move(t)),但因为Buf没有自定义移动构造,编译器会用默认的拷贝构造来处理:直接把原对象的m_data和m_size值复制给队列里的新Buf对象。
  3. 这时候,临时Buf对象(包括offer里的t)和队列里的Buf对象共享同一个内存指针。
  4. 当produce函数执行完毕,临时Buf对象被销毁,第一次调用析构函数释放了m_data指向的内存。
  5. 等main函数结束,EventQueue销毁内部队列时,队列里的Buf对象也会被销毁,此时再次调用析构函数去释放已经被free的指针,就会触发重复释放的异常。

解决方法

给Buf类添加移动构造函数,用来转移内存所有权,同时把源对象的指针置空,避免其析构时释放内存。另外,要显式禁用拷贝构造和拷贝赋值,防止意外的浅拷贝。

修改后的Buf类示例:

class Buf{
    const uint8_t *m_data;
    const size_t m_size;
public:
    // 构造函数
    Buf(const uint8_t *data, size_t size) : m_data(data), m_size(size) { }

    // 移动构造函数
    Buf(Buf&& other) noexcept : m_data(other.m_data), m_size(other.m_size) {
        // 把源对象的指针置空,让它析构时不会释放内存
        other.m_data = nullptr;
    }

    // 禁用拷贝构造和拷贝赋值
    Buf(const Buf&) = delete;
    Buf& operator=(const Buf&) = delete;

    // 移动赋值运算符(可选,根据需求添加)
    Buf& operator=(Buf&& other) noexcept {
        if (this != &other) {
            // 先释放当前对象的内存(如果有的话)
            free((void *)m_data);
            m_data = other.m_data;
            m_size = other.m_size;
            other.m_data = nullptr;
        }
        return *this;
    }

    size_t size() const { return m_size; }
    const uint8_t *data() const { return m_data; }

    ~Buf()
    {
        std::cout << "dtor called " << std::endl;
        // 只有指针不为空时才释放
        if (m_data != nullptr) {
            free((void *)m_data);
        }
    }
};

这样修改后,当你用std::move转移Buf对象时,会调用移动构造函数,把内存所有权转移给队列里的对象,源对象的指针被置空,析构时不会释放内存,也就不会出现重复释放的问题了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 14:51:03