Reputation: 16512
I'm trying to pass objects by value between threads with a queue.
Because there are multiple different objects, I made an abstract class.
class message
{
public :
virtual ~message(){};
};
then I have a subclass for each different type of message
class a_specific_message : public message
{
...
};
I read this tutorial for implementing the queue and I call it the following way:
concurrent_queue<message> queue;
a_specific_message m{1, 2, 3};
queue.push(m);
My problem is that I need to override the operator=
so the queue can clone the message
popped_value=the_queue.front();
I tried to add a virtual operator but it doesn't get called in the subclass.
I don't know how I could achieve something like this without passing object by reference.
Upvotes: 0
Views: 249
Reputation: 41750
In my opinion, polymorphism is not what you really want. An interface with nothing but a destructor and dynamic downcast usually means that you are using the wrong tool.
However, if polymorphism is what you want, let me propose std::unique_ptr
. You can have here you queue of pointers declared like that: concurrent_queue<std::unique_ptr<message>>
As you told in the comments:
I didn't want to use pointers because devs will have to remember to delete it in another thread. It could be easy to forget and have a memory leak.
Then I think std::unqiue_ptr
is the right thing for you.
If I translate your code, it would look like that:
concurrent_queue<std::unique_ptr<message>> queue;
auto m = std::make_unique<a_specific_message>(1, 2, 3);
queue.push(std::move(m));
Or simply:
concurrent_queue<std::unique_ptr<message>> queue;
queue.push(std::make_unique<a_specific_message>(1, 2, 3));
// Or
queue.push(new a_specific_message{1, 2, 3});
Then, to pop values:
auto popped_value = std::move(the_queue.front());
Then everything is deleted automatically, without having to remember to delete any pointer. std::unique_ptr
has been created for that purpose.
To avoid the explicit move in your code, you could have something like pop_value
in your queue, implemented like that:
T pop_value() {
auto value = std::move(the_queue.front());
the_queue.pop();
// use nrvo
return value;
}
So now, in your threads, you can safely do that:
{
auto some_message = queue.pop_value();
// stuff
} // some_message deleted here.
Upvotes: 2
Reputation: 136208
As @PeteBecker writes in the comments, concurrent_queue<message>
holds message
's by value. When a derived object instance is pushed into the queue it makes a copy of message
part only. Slicing occurs.
One way to make the queue hold objects of multiple types without resorting to pointers is to use a discriminated union, e.g. boost::variant
:
using message = boost::variant<specific_message_a, specific_message_b>;
No common base class for messages is required here.
The downside of this approach is that sizeof(message)
is sizeof
of the largest type in that boost::variant<>
template argument list.
Upvotes: 2