Re: Virtual dtor and placement new.

From: Tobias Güntner (fatbull_at_users.sourceforge.net)
Date: 08/16/04


Date: Mon, 16 Aug 2004 20:23:37 +0200

Giancarlo Niccolai wrote:

> The question is: how to use virtual destructors when dealing with placed
> memory?

Well, you somehow need to remember the type of the object.

My suggestion is the following:

// Basically calls the destructor.
// It is assumed that p is of type T*
template<class T>
void CallDestructor(void* p)
{
        static_cast<T*>(p)->~T();
}
typedef void (*DestructPtr)(void*);

// and then replace the placement new with

template<class T>
T* MyNew(void* mem)
{
        // Store a pointer to our "destructor"
        *static_cast<DestructPtr*>(mem) = CallDestructor<T>;

        // Calculate offset to place the object behind the pointer
        // TODO: ensure that *newMem is aligned properly!
        void* newMem = static_cast<DestructPtr*>(mem) + 1;

        // Placement-new the new object as usual
        return new(newMem) T;
}

void MyDelete(void* mem)
{
        // Now you would call the destructor as follows
        (*static_cast<DestructPtr*>(mem))(mem);
}

Note that it is still necessary that you remember a pointer to the
originally allocated object (which can be different, e.g. in the
presence of multiple or virtual inheritance)

It is possible to store this information in a smart-pointer, however.
For example boost::shared_ptr, where you can pass a functor that can do
additional cleanup when the memory is to be deleted (i.e. this functor
could then store a pointer to the original memory and call MyDelete
automatically).
Using a smart pointer seems to be the most reliable way IMHO.

Another approach might be to use thunks (see
http://www.pluralsight.com/articlecontent/cpprep0399.htm for an
example), but that is all very low-level and highly platform dependent.

You could as well derive your object from a special base class that
knows the destructor (works basically like the first approach, but is
not so low-level and more obvious. I guess I should have mentioned this
version first ;) )

struct Deletable
{
virtual void CallMyDestructor() = 0;
};
template<class T>
struct PlacementDeletable : public Deletable, public T
{
virtual void CallMyDestructor() { this->~T(); }
}

Now you have to store a Deletable* somewhere (or you could use
dynamic_cast to get it) and call the destructor through that object.

-- 
Regards,
Tobias


Relevant Pages

  • Re: C++0x atomic and reference counter
    ... before the memory block is reclaimed, ... that maps to a load-acquire in or before the destructor. ... and I assert that there is no ordering problem with the store ... speculation, ...
    (comp.programming.threads)
  • Re: map::clear()
    ... > is invoked from the maps destructor? ... The destructor may not actually call "clear", ... destructor could deallocate your own memory. ... it is often best to simply store the class by value. ...
    (microsoft.public.vc.stl)
  • Re: C++0x atomic and reference counter
    ... you opt for low-level ordering constraints then you have to specify ... as they cannot legally access the memory locations. ... Does it matter if the destructor is empty as in the example provided? ... and thus needs the acquire fence. ...
    (comp.programming.threads)
  • Re: Returning an unknown number of types/values
    ... >>second is freeing the memory. ... the object's destructor won't be called automatically. ... > works fine with and empty derived destructor as far as I can see. ... That gives us a pointer to buf_. ...
    (alt.comp.lang.learn.c-cpp)
  • Re: System.Timers.Timer
    ... I've wrote a class destructor, only when the user enable receiving device status, the destructor is never called and keeps running in memory raising the event ... application terminates successfully ...
    (microsoft.public.dotnet.framework)