为何此场景下Buf类的析构函数会被调用两次?
问题根源:缺少移动构造函数导致的浅拷贝
你的问题核心是Buf类没有定义移动构造函数和移动赋值运算符,编译器只能生成默认的拷贝构造——这会导致你以为的“所有权转移”变成了浅拷贝,最终同一个内存指针被两个Buf对象持有,触发两次析构。
具体过程拆解
- 在
produce函数里,你创建了一个临时Buf对象Buf(new uint8_t[msg_size], 10),并以右值形式传给offer函数。 - 进入
offer后,你调用push(std::move(t)),但因为Buf没有自定义移动构造,编译器会用默认的拷贝构造来处理:直接把原对象的m_data和m_size值复制给队列里的新Buf对象。 - 这时候,临时Buf对象(包括
offer里的t)和队列里的Buf对象共享同一个内存指针。 - 当
produce函数执行完毕,临时Buf对象被销毁,第一次调用析构函数释放了m_data指向的内存。 - 等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
相关产品推荐
相关产品推荐

