20

I have a std::queue that is wrapped as a templated class to make a thread-safe queue. I have two versions of this class: one that stores value types, one that stores pointer types.

For the pointer type, I'm having trouble deleting the elements of the queue on destruction. The reason is that I don't know a way to remove the items from the queue safely.

This reference states (vacuously, so I guess it doesn't actually STATE it) that the only way to remove elements from the queue is to call pop(). The reference also says that pop() calls the destructor for the item.

Well, this causes problems with my pointer types because they may or may not actually point to aggregates. If one of them points to an aggregate, they all will, but because the wrapper is templated, there is no guarantee which type (aggregated or non-aggregated) we are dealing with.

So, when pop() calls the destructor, what happens? How do I ensure that everything is being removed and the memory deallocation properly?

Lastly, my solution is using an older version of GCC for ARM9. I don't have control over this. I understand that there are libraries that have smart pointers and containers that would assist here, but they are off-limits for me.

Sam
  • 7,252
  • 16
  • 46
  • 65
San Jacinto
  • 8,774
  • 5
  • 43
  • 58
  • 1
    "Well, this causes problems with my pointer types because they may or may not actually point to aggregates" I don't understand what aggregates have to do with anything here. – Lightness Races in Orbit May 22 '11 at 14:12

3 Answers3

39

Pointers themselves don't actually have destructors, so calling pop() on a queue containing a pointer won't call the destructor of the object your pointer points to.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • Thanks for the answer. This is what I was wondering, if it wasn't clear from my rambling question. – San Jacinto Jan 04 '10 at 21:20
  • Greg, I appreciate the fast answer and still vote up, but I changed my acceptance to Neil's answer because it has a little more thorough explanation of what's happening. – San Jacinto Jan 04 '10 at 21:37
  • I agree. Although the line of thinking is very good :D. The first tell-tale sign should be that a pointer doesn't have a destructor. – Hassan Syed Jan 04 '10 at 21:40
  • 1
    @Hassan no, it doesn't have a destructor, but the compiler can keep track of the types and therefore can call delete if we are talking about a pointing type. I didn't think it was likely, but what ACTUALLY happens is what matters. – San Jacinto Jan 04 '10 at 22:01
  • Compilers call `T::~T()` when you destroy a _smart_ pointer to a `T`, e.g. std::auto_ptr or `std::tr1::shared_ptr`. That's pretty much the definition of _smart_ pointers. It doesn't do such things for raw pointers, if only to maintain C compatibility. – MSalters Jan 05 '10 at 15:11
23

Online sources are worth what you pay for them - get a proper reference like Josuttis's book. pop() does not "call the destructor" - it simply removes an element from the queue adaptor's underlying representation (by default a std::deque) by calling pop_front() on it. If the thing being popped has a destructor, it will be used when the popped object goes out of scope, but the queue class has nothing to do with it.

  • Thx for clarifying, I had trouble wrapping my head around that statement: "This calls the removed element's destructor" – Emile Vrijdags Jan 04 '10 at 21:33
  • assuming the popped object was inserted into a smart pointer of some form. – Hassan Syed Jan 04 '10 at 21:33
  • Assuming nothing. If the object has a destructor it will be called (but not by pop()), if not, well, there's no destructor to call. –  Jan 04 '10 at 21:35
  • destructors only get called on items automatically -- when they go out of scope -- if they have smart pointers..... am I missing something here ... ? – Hassan Syed Jan 04 '10 at 21:37
  • 1
    Is it really fair to say "not by pop()"? That's like saying main does nothing, because main only calls other functions. But main does everything. – GManNickG Jan 04 '10 at 21:38
  • Yes - I don't think you understand how smart pointers work. But that;s a subject too big for an SO comment. –  Jan 04 '10 at 21:40
  • @GMan Fair enough. But "calling the destructor" is usually reserved in C++ parlance for when you actually do call it - for example when you use placement new. In this case, all pop does is remove an element from a deque. –  Jan 04 '10 at 21:42
  • WTF ? , an object can be new'd or stack allocated, the former will not automatically release on OOS without a smart ptr -- your totality does not hold. – Hassan Syed Jan 04 '10 at 21:43
  • and the guy is talking about both pointers and value types ... in a c++ context. I would say it is fair to assume they might be new'd. – Hassan Syed Jan 04 '10 at 21:45
  • @Hassan My totality is just fine. Please re-read what I wrote. –  Jan 04 '10 at 21:45
  • @Neil In my program, I call pop(). The very next line in my code, let's pretend, is i++; Has the destructor been called by the time i++ happens? I'm not asking if the pop() function called the destructor, I'm asking if by virtue of pop() being called if it's been removed. – San Jacinto Jan 04 '10 at 21:47
  • 3
    @Hassan: containers always, always destroy their contained objects when they're removed from the container. Always. Doesn't matter whether the contained object is a user-defined type, an integer, a pointer, or a smart pointer. It's destroyed (or nothing happens, if it has no destructor). The only question then, is what destroying the popped object actually does. For a pointer, it does nothing. A `new`d object cannot ever be the contents of a container, whether "inserted into a smart pointer" or not. A pointer to it might be the contents of a container, but that's a completely different thing. – Steve Jessop Jan 04 '10 at 21:47
  • @GMan Just to clarify if I have a deque of strings and do d.pop_front() am I calling the destructor for the string I pop? Well yes, but most C++ programmers don't think of it that way. I think :-) –  Jan 04 '10 at 21:49
  • 1
    To re=phrase Neils comments. When you pop an object (from a standard container) the destructor will be called on the object poped. A pointer object does NOT have a destructor (what it points at may have a destructor but that is not what is stored in the queue). So a pointer object will simply be reoved from the queue. – Martin York Jan 04 '10 at 21:50
  • The popped object could be the copy used in the queue, or the copy returned. In either case only a boost smart pointer would fix your totality. – Hassan Syed Jan 04 '10 at 21:51
  • @San Jacinto A good question! I suggest starting another question and posting the exact code for at least the function that you are asking about. Otherwise there is no clear answer. –  Jan 04 '10 at 21:52
  • @steve you can always dereference the new'd pointer to put the item into the queue :/ and this is a common idiom :D – Hassan Syed Jan 04 '10 at 21:53
  • @Martin Assuming you are talking to me, the corrective for an obtuse and unclear answer is to post an accute and transparent one. –  Jan 04 '10 at 21:55
  • @Martin, wish I could change to (-1) – Hassan Syed Jan 04 '10 at 21:57
  • I'm tempted to start shouting things like DON'T PANIC MR MAINWARING! Really, there are three (?) issues here and all should be addressed in separate questions. –  Jan 04 '10 at 21:57
  • Thanks, Neil and Steve. Your comments (and answer) combined have cleared it up for me. – San Jacinto Jan 04 '10 at 21:59
  • @Hassan: that doesn't put the object in the queue, it puts a copy of it in the queue. – Steve Jessop Jan 04 '10 at 22:12
  • @Hassan: what do you mean, "The popped object could be the copy used in the queue, or the copy returned"? `pop()` doesn't return anything. – Steve Jessop Jan 04 '10 at 22:14
  • 1
    @Neil: I've just realised that this business about what calls what, is probably the reason that everyone (including me) ends up writing all their documentation in the passive voice, and it comes out with a Flesch-Kincaid equivalent older than Methuselah. "the object is removed from the underlying container before pop() returns, with the consequence that its destructor (if any) is called before pop() returns" vs. "pop() removes the object from the underlying container, hence calls its destructor (if any)". – Steve Jessop Jan 04 '10 at 22:44
  • 1
    @Steve I don't see what is wrong with "removes the object from the underlying container" - the thought of reproducing an explanation of destructor semantics in each case is frightening. And in fact this is the way the C++ Standard is written. –  Jan 04 '10 at 22:50
  • @Martin It is not accepted SO practice to vote people down for being correct, no matter how difficult you may find their answers. –  Jan 04 '10 at 22:57
6

"How do I ensure that everything is being removed and the memory deallocation properly?"

If you absolutely have to store pointers in your queue, and you want them to be automatically freed when they're poped, then instead of a queue of pointers, you need a queue of objects which store a pointer, and delete it in their destructor. You could for example use a queue of shared_ptr. shared_ptr isn't in the standard library, but it's part of TR1 and is widely available.

Otherwise, it's the responsibility of the caller to delete the object:

T *off = q.front();
q.pop();
delete off;

The summary is that containers of pointers to dynamically allocated objects are a bit awkward. If you can design your program so that containers store copies of your objects, instead of pointers to dynamic objects, then do so. Failing that, you're responsible for resource ownership, not the container. STL containers know nothing about ownership, they just copy and destroy their value_type. Copying and destroying pointers does nothing to the objects they point to.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • I thought of this but rejected it because I don't know the future uses of the templated class. I don't want to give raw access to the queue for users b/c of thread safety concerns, and I can't guarantee that the users would use the idea you just mentioned, so I decided against it. – San Jacinto Jan 04 '10 at 21:40
  • If your class is using its template parameter as the value type for a hidden queue, then it should probably do what containers do: if the user allocates anything, then the user has to arrange to eventually free it (for instance by using a smart pointer as the value type). For instance if it's an event queue, and the user insists on using a plain pointer, then they could perhaps free the object in the event handler. But it's not the container's problem, and IMO you shouldn't try to write containers which treat pointers specially. – Steve Jessop Jan 04 '10 at 22:03
  • ... admittedly, this just punts the problem on to the next guy. But there are good reasons not to confuse containers with ownership, not least of which is that someone might want to use your container to store pointers, without giving you ownership (because they point to static data or something). I assume boost::ptr_deque can be used as the underlying container for a queue, so you could have two template parameters (like queue), and let the user use that if does what they want. – Steve Jessop Jan 04 '10 at 22:19
  • Normally I'd agree with this sound logic, but for my immediate use, the removal of elements happens in anomalous situations. In my class, the items are removed only when the destructor is called, which means there was likely a large failure for this particular subsystem. I'm essentially using this queue as the middleman for producer/consumer, and if there have been items that are produced, they need to be disposed of properly and right away if there has been an error this big. Otherwise, the driver (consumer) thread will continue to operate and things will go badly in the hardware sense. – San Jacinto Jan 05 '10 at 12:20