Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Removing from STL std::queue without destructing the removed object?

Tags:

c++

stl

queue

All the documentation I can find on the STL containers (both queue and list) say that for any of the remove functions the removed object's destructor is called. This means that I can't use std::queue any time I want a queue that's simply a list of objects needing some operation performed on them.

I want to be able to add objects to the queue when they are waiting in line for me to do something to them. Then I want to remove them from it when I've finished with them, without destroying the object in question. This doesn't appear to be possible from the documentation I've read. Am I misreading the documentation? Is there another type of queue in the STL other than the basic "queue" that doesn't call the removed object's destructor on a call to pop_front?

Edit to clarify: In my case I'm using a list of pointers. Something like this:

   dbObject *someObject;
   queue<dbObject *> inputQueue;
   inputQueue.push_back(someObject);

   ...

   dbObject *objectWithInput = inputQueue.front();
   //handle object's input...
   inputQueue.pop_front(); // Remove from queue... destroyed now?
like image 218
Daniel Bingham Avatar asked Oct 03 '09 17:10

Daniel Bingham


3 Answers

If you put pointers to objects in the queue (and any other STL container), the pointers won't get deleted when you remove them.

To elaborate: when you use std::queue and remove an object the destructor of some_obj* is called. But the destructor for plain pointer (or any POD type - int, char, etc) is empty, no-op. The fine line here is that the destructor for some_obj* is very different from the destructor for some_obj.

like image 54
sbk Avatar answered Nov 09 '22 01:11

sbk


STL containers have value semantics. When you push an object into an STL container, the STL container keeps it's own copy of the object, and when the object (internal copy) is removed from the container it is destroyed.

If you used a container of a proxy type, as raw pointers, smart pointers (shared_ptr, weak_ptr), or adapters (as boost::reference_wrapper), then the STL container will destroy the proxy but not the type. Choosing one over the others is usually a matter of how you want to deal with resources.

The most common idiom is using raw pointers, but they don't explicit who is in charge of destruction (the code that pulls from the container should delete the pointer, or the resource is handled somewhere else?).

Modern usage moves towards the shared_ptr approach, as it dilutes the ownership problem. The object will be guaranteed to be alive when you take it out of the container, and if nobody else holds a shared_ptr then the object will automatically be deleted when the local shared_ptr goes out of scope. Using a weak_ptr will keep the ownership in the original code, but will allow you to check for validity of the pointer (if it was deleted) before usage. This could allow you to avoid performing the operation on an object that will be removed right away.

The problem with the shared_ptr/weak_ptr approach is that it forces you to use shared_ptr to hold the original resource. This means that you will not be able to put a pointer into a subobject (member attribute) of another class without redesigning the class to hold the attribute through a shared_ptr, and that will have other implications (the attributes will no longer be contiguous in memory, more dynamic allocation operations will be required...)

A technique that is hardly seen is using adapters as boost::reference_wrapper<>. A reference wrapper is a proxy object that contains a reference to the original object and is itself copyable. The advantage over plain raw pointers is that reading the code it is clear that the resource is managed outside of the queue: the code that pulls data from the queue does not need to delete the object. The advantage over the smart pointer approach is that you do not need to redesign other parts of your system to use smart pointers. The disadvantage is that, as in the raw pointer approach, you must ensure that the lifetime of the referred object outlives the reference in the container manually.

like image 7
David Rodríguez - dribeas Avatar answered Nov 09 '22 01:11

David Rodríguez - dribeas


class someobj_t {};

std::queue<someobj_t> q;
...

someobj_t ppd = q.front(); // ppd is not a reference
q.pop();

// ppd now contain removed object

If you don't want someobj_t to be copied you could use std::queue< shared_ptr<someobj_t> >.

like image 4
Kirill V. Lyadvinsky Avatar answered Nov 09 '22 01:11

Kirill V. Lyadvinsky