【发布时间】:2022-10-15 18:41:27
【问题描述】:
在下面的代码中,我无法理解为什么类 Buf 的析构函数被调用了两次。调试时,我可以看到它在运行线程离开函数Test::produce 时第一次被调用。第二次是离开主要功能时,本质上是在破坏类EventQueue时,这是我所期望的。
但是,我不明白为什么在离开函数Test::produce 时会调用Buf 的析构函数。具体来说,我创建类Buf 作为右值,将其传递给EventQueue 并移至其内部缓存。事实上,这给我带来了一个问题,即我最终尝试两次释放相同的指针,这会引发异常。
template<typename T>
class EventQueue{
public:
void offer(T&& t) {
m_queue.try_emplace(std::this_thread::get_id()).first->second.push(std::move(t));
};
std::unordered_map<std::thread::id, std::queue<T>> m_queue;
};
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) { }
size_t size() const { return m_size; }
const uint8_t *data() const { return m_data; }
~Buf()
{
std::cout << "dtor called " << std::endl;
free((void *)m_data);
}
};
class Test{ and was not expecting
public:
Test(shared_ptr<EventQueue<Buf>> buf) : m_buf(buf)
{
std::thread t1 = std::thread([this] { this->produce(10); });
t1.detach();
};
void produce(int msg_size) {
m_buf->offer(Buf(new uint8_t[msg_size], 10));
}
std::shared_ptr<EventQueue<Buf>> m_buf;
};
int main()
{
auto event_queue = std::make_shared<EventQueue<Buf>>();
Test tt(event_queue);
return 0;
}
【问题讨论】:
-
当您将值从一个对象移动到另一个对象时,原始对象仍然存在,但处于已移出状态。无论是否移出,它都会在其生命周期结束时被销毁。移动对象不会也不会导致其析构函数被跳过。
-
m_buf->offer(Buf(new uint8_t[msg_size], 10));表示一个临时的Buf对象,该对象被移动到内存位置EventQueue<Buf>::m_queue需要它去...这就是为什么你应该总是删除默认的复制/移动构造函数/赋值运算符,通过定义移动构造函数/赋值运算符作为已删除,除非您知道按成员移动可以解决问题,或者您自己实现复制和/或移动语义(或者已经有一个成员导致隐式定义的特殊成员被删除)... -
您的
Buf类包含严重的错误,因为它不遵循 0/5/3 的规则:stackoverflow.com/questions/4172722/what-is-the-rule-of-three -
我看到
new,但没有看到delete。我确实看到了free,但情况可能更糟。new[]与delete[]配对。没有什么可以指望完成这项工作。我们可以说服您使用std::vector吗? -
std::cout << "dtor called " << std::endl;-- 您应该打印this的值,而不仅仅是一条简单的消息,即std::cout << "dtor called on object " << this << std::endl;您很可能没有删除您认为正在删除的同一个对象。这不是析构函数被调用两次的问题——析构函数被调用一次,而是你正在处理不同的对象。
标签: c++