tags:

views:

593

answers:

5

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?
+3  A: 

How about using a list of pointers to the objects?

therefromhere
Wouldn't that still call the deconstructor? That is what I am using, most of my objects are pointers to an object stored else where in code.
Daniel Bingham
+4  A: 
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> >.

Kirill V. Lyadvinsky
I'm using a list of pointers.
Daniel Bingham
Then real object will not be destroyed on erase.
Kirill V. Lyadvinsky
+12  A: 

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.

sbk
Exactly. And just for reference, if you actually do ever want to container to delete the pointed-to objects, then you would need to either store some kind of smart pointer or else use something like boost's pointer-containers.
TheUndeadFish
+5  A: 

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.

David Rodríguez - dribeas
I like the way you explained and worded this answer, very useful and very complete.
joshperry
+1  A: 

Think of item's in a container being "in-scope" of that container, when an item is removed from a container it is just like leaving the scope of a function. If the variable is a pointer nothing happens to the item when leaving the scope. If variable is a stack local then destructor will will be automatically called upon leaving the scope.

Storing pointers in containers have the same downfalls as allocating into a local raw pointer, the memory is not cleaned up automatically. In a function if you don't delete the pointer or transfer ownership by returning it then you have a memory leak.

When storing raw pointers in a container the ownership can become a bit ambiguous and leaks can easily happen. Take a look at tr1::shared_ptr and store those in the containers instead.

std::unique_ptr in C++0x would also be a good solution for storing a pointer in a stdlib container when it is available.

joshperry