In the following code , I am not able to understand why the destructor of the class Buf
is invoked twice. When debugging I can see that it is being invoked the first time when the running thread is leaving the function Test::produce
. The second time is when leaving the main function which essentially is when destructing the class EventQueue
, something I would expect.
However, I dont understand why when leaving the function Test::produce
the destructor of Buf
is invoked. Specifically, I create the class Buf
as a r-value passing it to the EventQueue
and move to its internal cache. In fact, this has created me the problem that I end up trying ti free the same pointer twice which throws an exception.
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;
}
CodePudding user response:
Every temporary object created must always be destructed as well. The problem however, is not about how many objects were destructed, but that raw pointers which your class instance "owns" (and is supposed to delete at some point) are not quite compatible with implicit move constructor, because it merely does member-wise std::move
:
For non-union class types (
class
andstruct
), the move constructor performs full member-wise move of the object's bases and non-static members, in their initialization order, using direct initialization with an xvalue argument.
For any built-in types (including raw pointers), it means that nothing actually happens.
Thus, you have to nullify the source object's member raw pointer explicitly (or use smart pointers):
Buf(Buf&& other) noexcept : m_data{ std::exchange(other.m_data, nullptr) }, m_size{ other.m_size } {}