tags:

views:

1051

answers:

11

If I call a destructor explicitly ( myObject.~Object() ) does this assure me that the object will be appropriately destroyed (calling all child destructors) ?

Ok some code:

class Object
{
   virtual ~Object()
   {}
};

class Widget : public Object
{
   virtual ~Widget()
   {}
};

...
Object* aWidget = new Widget(); //allocate and construct
aWidget->~Object(); //destroy and DON'T deallocate

I know I could just delete the object, but I don't want to. I want to keep the allocated memory handy as an important optimization.

Thanks!

+6  A: 

Yes, a destructor, even when called explicitly, will destroy its subobjects properly.

As you seem to realize, it's a rare action to do, but perhaps as part of a well tested and documented library it may be useful. But document (and profile) it since even though it's valid and safe, every maintainer (including you) will never feel comfortable with it.

SPWorley
+8  A: 

Yes. But holy smokes, are you sure about this? If so I would use placement new to construct your Widget. Using placement new and then explicitly calling the destructor is an acceptable, if unusual, idiom.

Edit: Consider allocating the memory yourself manually rather than using new to allocate the first object and then re-using its memory afterward. That allows you complete control over the memory; you could allocate big chunks at a time, for instance, rather than allocating a separate block of memory for each Widget. That'd be fair savings if memory really is such a scarce resource.

Also, and perhaps more importantly, you'd then be doing placement new "normally", rather than this hybrid regular new/placement new solution. I'm not saying it won't work, I'm just saying it's a rather, ah, creative solution to your memory problem.

John Kugelman
Yeah I know the code as such is not very intuitive, but the memory handling happens behind a source/sink pattern, so the user just requests widgets, they get newed if there are none left, and they get a placement new if there is memory left.
Robert Gould
+2  A: 

Running the destructor does not free memory used by the object being destructed - the delete operator does that. Note, however, that the destructor may delete "child objects" and their memory will be freed as per usual.

You need to read up on placement new/delete as this allows you to control memory allocation and when constructors/destructors run.

See here for a little info:

http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.9
Daniel Paull
I know, I know, I DON'T want to free the memory, I just want to destroy the object.
Robert Gould
ok, ok - use placement new to match your explicit call to the destructor. I don't understand why you can't reuse the old widget, assigning new values to its members. Why construct and destruct as you are doing? For example, if you are just trying to avoid the heap allocation, allocate your new widget on the stack and then assign (operator ==) to the existing widget that was heap allocated.
Daniel Paull
The issue is I need to call the destructor, because it happens to do non trivial things if widget gets subclasses
Robert Gould
How can your approach possibly work if Widget can be subclassed? The subclasses would, under most circumstances, be different sizes. It feels like you're playing with fire with this approach.
Daniel Paull
Because the subclassing of widget is limited to the library code, not user code, and widgets of each type are stored in different pools, so the size of a determined widget family will not change (except for the recompile issue pointed above)
Robert Gould
Ok, so the type of Widget is the final class. Tell me why you need to run destructors and constructors rather than using an assignment operator? Also, why are you allocating individual Widgets? Surely a std::vector< Widget > (not a std::vector< Widget* > ) and the use of assignments would give you the optimisation you are looking for.
Daniel Paull
... correction - perhaps a std::deque<Widget> as the vector requires the elements to be relocatable, which is not true when you are return points to the elements to calling code.
Daniel Paull
yeah the std::vector won't cut it. Anyways the idea of delegating the whole business to a deque is something I had considered, because its a good idea, but to deepen my knowledge of C++ I began investigating this perhaps, unorthodox solution.
Robert Gould
+3  A: 

Yes. A destructor calls any member destructors in LIFO order, then base class destructors, and there is no way to prevent it from calling these destructors*. The object stack is guaranteed to unwind.

Initialization and finalization are separated from memory allocation and deallocation in C++ exactly so that when the special case arises, there is an unambiguous syntax in which the application-programmer can express his or her intent to the compiler.

Edit:

  • I suppose that by invoking abort() or longjmp() one could, in fact, prevent the member and base class destructors from running.
Thomas L Holaday
+13  A: 

The answer is... nearly always.

If your object has a non-virtual destructor, and is then sub-classed to add child elements that need freeing... then calling the destructor on the object base class will not free the child elements. This is why you should always declare destructors virtual.

We had an interesting case where two shared libraries referenced an object. We changed the definition to add child objects which needed freeing. We recompiled the first shared library which contained the object definition.

HOWEVER, the second shared library was not recompiled. This means that it did not know of the newly added virtual object definition. Delete's invoked from the second shared library simply called free, and did not invoke the virtual destructor chain. Result was a nasty memory leak.

the.jxc
interesting point!
Robert Gould
Indeed! Good catch!
Andres Jaan Tack
Good point, but your problem was caused by not recompiling enough libraries :-)
Justicle
Good point :) But still no need to always declare a destructor virtual. Only needed when you are utilizing polymorphism.
Magnus Skog
A: 

Why destroy it at all? Just write over the memory? Are you wanting logic to execute to gracefully handle releasing resources? I am going to state emphatically that this is an abuse of the language and not a good idea.

ojblass
Opinion noted, but this does not answer the question.
Justicle
Thanks for the preoccupation, but my question is a very fundamental question, and if the behavior is not depending on undefined behavior, or compiler dependent implementations, it should be perfectly valid, as strange as it may be.
Robert Gould
+4  A: 

Please save yourself some real headaches and use the Boost Object Pool, which sounds like an existing implementation of your source/sink pattern. It will allocate large chunks of memory, slice them into the correct size for your object and return them to you (after calling the constructor). When you delete objects, they have their destructor called and are put into a linked list of objects for re-use. It will grow and shrink automatically and ensure that instances of your objects tend to be close together in memory.

If nothing else, it is a good example implementation of placement new and explicit use of constructors that you could study.

Tom Leys
I wish, but I can't use boost. Anyways I'll look at the implementation
Robert Gould
+4  A: 

Yes it will call all the child destructors so it will work as you are expecting.

The destructor is just a function after all, it just so happens that it gets called when objects are deleted.

Therefore if you use this approach be careful of this:

#include <iostream>

class A
{
public: 
    A(){};
    ~A()
    {
     std::cout << "OMG" << std::endl;
    }
};

int main()
{
    A* a = new A;
    a->~A();
    delete a;
    return 0;
}

output:
OMG
OMG

The destructor is called a second time when delete is actually called on the object, so if you delete pointers in your destructor, make sure that you set them to 0, so that the second the destructor is called nothing will happen (as deleting a null pointer does nothing).

0xC0DEFACE
A: 

I would consider overriding new for the objects you want special allocation and deallocation behaviour for - after all that's what it's for. A hybrid scheme of normal new and explicitly calling destructors sounds like a recipe for future headaches. For starters, any memory leak detection strategy is going to get thrown way off.

markh44
+2  A: 

STL containers do this. In fact, an STL allocator must provide a destroy method that calls an object's destructor (allcators also provide a deallocate method to deallocate the memory that used to hold an object). However, the advice from Stroustrup (The C++ Programming Language 10.4.11) is

Note that explicit calls of destructors ... should be avoided wherever possible. Occassionally, they are essential. ... A novice should think thrice before calling a destructor explicitly and also ask a more experienced colleague before doing so.

Max Lybbert
In all fairness - asking more experienced people is basically what he's doing.
MSalters
Yeah I've been bit by this behavior in STL containers, which is why I was wondering about the implementation details
Robert Gould
+1  A: 

Calling the destructor is fine. However, beware of the type you're calling it on. If that type doesn't have (didn't inherit) a virtual destructor, you might get unexpected behaviour.

Also, as mentioned, the destructor does not free any memory, but I guess that's the reason you want to call it manually in the first place.

Plus, unless I'm mistaken, calling the destructor manually is the only option you have if you used placement new to call the constructor.