Virtual dtor and placement new.

G

Giancarlo Niccolai

Hello all.

I have peeked through the FAQ and all relevant links, and also through
Stroustrup book, but I have not been able to find an answer, so I have to
post here as a last resort.

It makes sense that if you have virtual destructors, they are eventually
used in the explicit destructor call when using the placement new semantic:

class A {
....
virtual ~A() { cout <<"one" <<endl; }
};

class B: public A {
....
virtual ~B() { cout << "two" << endl; }
};

....

A *mem = static_cast<A*>(new( good_memory ) B);

// Now we have a B item in a placed good memory, which is pointed
// by a base class A*

Now, using the semantic

mem->~A();
free_good_memory( mem );

won't do, as it won't scan the B object Vtable and will just feed the mem
object in the A class destructor.

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

TIA,

Giancarlo Niccolai
 
V

Victor Bazarov

Giancarlo said:
I have peeked through the FAQ and all relevant links, and also through
Stroustrup book, but I have not been able to find an answer, so I have to
post here as a last resort.

It makes sense that if you have virtual destructors, they are eventually
used in the explicit destructor call when using the placement new semantic:

class A {
...
virtual ~A() { cout <<"one" <<endl; }
};

class B: public A {
...
virtual ~B() { cout << "two" << endl; }
};

...

A *mem = static_cast<A*>(new( good_memory ) B);

There is no need for the cast. Conversion from a pointer to derived to
a pointer to base is implicit and provided by the language.
// Now we have a B item in a placed good memory, which is pointed
// by a base class A*

Now, using the semantic

mem->~A();
free_good_memory( mem );

won't do, as it won't scan the B object Vtable and will just feed the mem
object in the A class destructor.

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

I don't think there is an answer. There is, however, a proposal to
introduce "placement delete", probably specifically for that purpose.

Victor
 
A

Alf P. Steinbach

* Giancarlo Niccolai:
How to use virtual destructors when dealing with placed memory?

Short answer is: don't use placement new.

Especially, in light of the use of casting in your code,

A *mem = static_cast<A*>(new( good_memory ) B);

which is 100% unnecessary and misleading, absolutely don't use
placement new.

But to answer the question of "how", technically one approach is

#include <iostream> // std::cout

static char rawMemory[0x400];

class A
{
public:
virtual ~A() { std::cout << "~A()\n"; }
static void* operator new( size_t size )
{
std::cout << "Allocating " << (unsigned long) size << "
bytes.\n";
return rawMemory; // Assume suitable alignment.
}
static void operator delete( void* ) {}
};

class B: public A
{
double d;
public:
virtual ~B() { std::cout << "~B()\n"; }
};

int main()
{
A* p = new B;
delete p;
}

If you do provide an operator new( size_t ) for a class you should
probably also provide an operator new[] as well as the identity
form of placement new, but as already mentioned twice: don't.
 
G

Giancarlo Niccolai

Alf said:
* Giancarlo Niccolai:

Short answer is: don't use placement new.

Short reply is: I have to. And I know what I am doing and why.
Especially, in light of the use of casting in your code,

A *mem = static_cast<A*>(new( good_memory ) B);

which is 100% unnecessary and misleading, absolutely don't use
placement new.

Yes, It is 100% unnecessary. It was there to pose the emphasis on the fact
that B and A are one the base class of the other in a visual way; and
anyhow there is no need to correct a working thing, and to suppose that as
I have not been minimal, but explicit, I am not knowing what I am doing.
Please, don't teach me basics, I know them.

What I didn't know, and I thank you for, was:
virtual ~A() { std::cout << "~A()\n"; }
static void* operator new( size_t size )
{
std::cout << "Allocating " << (unsigned long) size << "
bytes.\n";
return rawMemory; // Assume suitable alignment.
}
static void operator delete( void* ) {}
};

Actually, it also works with overloaded:

class myFunAllocator {
....
};

// overload new with myFunAllocator;

class A {
//... no new.... operator
void operator delete( void * ) {
... do something that works with myFunAllocator;
}
};

A *a = new( myFunAllocator ) B;

delete a; // actually calls B destructor AND cleanly free memory with
// myFunAllocator.

This works also if using a placement new with placed new instead of an
overloaded new.

What I can't just take off of my head is: if this works (and it should,
given C++ rules), why then is said (in FAQ, in BS book, other books and
wherever I look) that placement new MUST be "closed" with explicit
destructor call? Is there something in the definition of the delete
operator that may fail if using it this way (i.e. overloading it and
freeing the mem yourself).

Thanks again,
Giancarlo Niccolai.
 
V

Victor Bazarov

Giancarlo said:
[...]
What I can't just take off of my head is: if this works (and it should,
given C++ rules), why then is said (in FAQ, in BS book, other books and
wherever I look) that placement new MUST be "closed" with explicit
destructor call? Is there something in the definition of the delete
operator that may fail if using it this way (i.e. overloading it and
freeing the mem yourself).

I think it comes from the notion that creation and destruction of any
object are the two operations that define its lifetime. You cannot
simply free the memory the object allocates, you have to tell the system
(and the object itself) that the object goes out of existence by calling
(or causing the invocation of) the object's d-tor.

Victor
 
A

Alf P. Steinbach

* Giancarlo Niccolai:
I have not been minimal, but explicit, I am not [not] knowing what I
am doing. Please, don't teach me basics, I know them.

Sorry about that; it's very difficult to know unless stated explicitly.

What I didn't know, and I thank you for, was:


Actually, it also works with overloaded:

class myFunAllocator {
...
};

// overload new with myFunAllocator;

class A {
//... no new.... operator

Uh oh. Should have


void* operator new( size_t size, myFunAllocator const& a )
{
...
}


to match the usage and restrict client code to The Right Way.

Now I remember both that there is no placement delete call syntax, and that
old MFC had a bug where placement new was defined without definining a
corresponding placement delete, resulting in a memory leak in debug builds.
Checking... Oh yes, if the constructor throws during a placement new call
then placement delete is called with the same "placement" args to
deallocate, and this was where the MFC bug was -- so you also need


void operator delete( void*, myFunAllocator )
{
// Used automatically when the constructor throws, §5.3.4/20.
}


as well as
void operator delete( void * ) {
... do something that works with myFunAllocator;
}
};

A *a = new( myFunAllocator ) B;

delete a; // actually calls B destructor AND cleanly free memory with
// myFunAllocator.

This works also if using a placement new with placed new instead of an
overloaded new.

What I can't just take off of my head is: if this works (and it should,
given C++ rules), why then is said (in FAQ, in BS book, other books and
wherever I look) that placement new MUST be "closed" with explicit
destructor call? Is there something in the definition of the delete
operator that may fail if using it this way (i.e. overloading it and
freeing the mem yourself).

I don't know; since I don't use this (have only written allocators in
PL/M-86, assembly and Pascal, never C or C++... ;-)) I'm no expert.
 
?

=?ISO-8859-1?Q?Tobias_G=FCntner?=

Giancarlo said:
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.
 
M

Max M.

Victor said:
I don't think there is an answer.  There is, however, a proposal to
introduce "placement delete", probably specifically for that purpose.

GCC does indeed call B's destructor, though. I've always thought this
behaviour was standard (and I am, in fact, relying on it in a few places).
Too bad. It doesn't look very logical, though, does it?

Max
 
R

Rob Williscroft

Giancarlo Niccolai wrote in in comp.lang.c++:
Hello all.

I have peeked through the FAQ and all relevant links, and also through
Stroustrup book, but I have not been able to find an answer, so I have
to post here as a last resort.

It makes sense that if you have virtual destructors, they are
eventually used in the explicit destructor call when using the
placement new semantic:

class A {
...
virtual ~A() { cout <<"one" <<endl; }
};

class B: public A {
...
virtual ~B() { cout << "two" << endl; }
};

...

A *mem = static_cast<A*>(new( good_memory ) B);

// Now we have a B item in a placed good memory, which is pointed
// by a base class A*

Now, using the semantic

mem->~A();
free_good_memory( mem );

won't do, as it won't scan the B object Vtable and will just feed the
mem object in the A class destructor.

Why ? / Who says this ?

Note Standard C++ has no concept of "Vtable", this is important as the
symantics of virtual destructors (or member-functions or base-classes)
are *not* defined in terms of virtual tables.
The question is: how to use virtual destructors when dealing with
placed memory?


#include <new>
#include <cstdio>

using std::printf;

struct A
{
virtual ~A() { printf( "~A()\n" ); }
};

struct B: A
{
virtual ~B() { printf( "~B()\n" ); }
};

int main()
{
char good_memory[100];
A *mem = new ((void *)good_memory) B();

mem->~A();
}

On Every compiler I tried (*):

output:
~B()
~A()

*) MSVC 7.1, g++ 3.4 and 3.2 and CBuilderX 6.0 (EDG/preview).

Rob.
 
A

Alf P. Steinbach

* Rob Williscroft:
struct A
{
virtual ~A() { printf( "~A()\n" ); }
};

struct B: A
{
virtual ~B() { printf( "~B()\n" ); }
};

int main()
{
char good_memory[100];
A *mem = new ((void *)good_memory) B();

Cast is not necessary.
mem->~A();
}

On Every compiler I tried (*):

output:
~B()
~A()

*) MSVC 7.1, g++ 3.4 and 3.2 and CBuilderX 6.0 (EDG/preview).

$12.4/12 lists just about the same example and requires the output
given above, so it is mandated by the Holy Standard (one interesting
tidbit, given "typedef B B_alias;" the call b_ptr->~B_alias() is
valid while b_ptr->B_alias::~B_alias() is invalid...).

I didn't know that you get a virtual call here (I don't use this), but still
recommend using member operator new and delete instead of direct placement
new, mainly because a class should encapsulate correct usage.
 
G

Giancarlo Niccolai

Max said:
GCC does indeed call B's destructor, though. I've always thought this
behaviour was standard (and I am, in fact, relying on it in a few places).
Too bad. It doesn't look very logical, though, does it?

Max

Actually, it doesn't, as ~A() semantically means exactly "call the method
named ~A(), which is accidentally A class destructor". As B() class
destructor is called ~B(), and not ~A(), even if they share the same Vtable
entry, they have actually two different symbolic name. Differently, if a
thing like

mem->~mem();

existed, which did not call the destructor by its symbol, but by its Vtable
entry, then the thing would logically work, but p->~A() is semantically
bound to call the function ~A() which is btw the A class destructor.

That was the root of my doubts; how to call the destructor by its vtable
entry, and not by its symbol-class name...

Anyhow the delete operator overloading proposed by Victor (thanks again)
works just fine;

delete p;

if p has a virtual destructor AND a static delete() operator declared in its
class, will call the virtual destructor AND THEN free the memory using the
code you feed in delete operator, exactly as wished.

Giancarlo.
 
G

Giancarlo Niccolai

Victor said:
Giancarlo said:
[...]
What I can't just take off of my head is: if this works (and it should,
given C++ rules), why then is said (in FAQ, in BS book, other books and
wherever I look) that placement new MUST be "closed" with explicit
destructor call? Is there something in the definition of the delete
operator that may fail if using it this way (i.e. overloading it and
freeing the mem yourself).

I think it comes from the notion that creation and destruction of any
object are the two operations that define its lifetime. You cannot
simply free the memory the object allocates, you have to tell the system
(and the object itself) that the object goes out of existence by calling
(or causing the invocation of) the object's d-tor.

Victor

Ok, I buy your point; but what troubles my mind is "why the book(s) says
that the only way to kill an object that you created with new(p) is to call
its destructor explicitly?". In other words, why they do not suggest about
the delete operator overloading? For "the books" I mean current literature
and ALL the source I have come across, including BS manual, in which it is
explicitly said not to use any other method than

p->~A();
yourFuncToFreeMem(p);

in this case.

A delete overload operator, as you correctly suggested, seems the logically
and semantically right solution based on the C++ language definition. And
moreover, it works. Then WHY the book(s) (read: official literature) does
not only not mention it, but also strongly encourage another, less C++ish
solution?

Probably the issue is not even worth to be dug, the fact that virtual
destructor + delete overloading does it fine should be enough, yet I ask if
this is just a hole in the current literature, a missing spot, a frogotten
topic, or if there is a deeper reason why BS and all the following official
sources explicitally say that the only right way to cause the termination
of a placed-new allocated item is the explicit destructor call + memory
management function call.

Giancarlo.
 
G

Giancarlo Niccolai

Alf P. Steinbach wrote:

Uh oh. Should have


void* operator new( size_t size, myFunAllocator const& a )
{
...
}


to match the usage and restrict client code to The Right Way.

Yes, in fact I said "it works *also*", meaning "a suboptimal way, but even
if suboptimal, working anyhow"...
Now I remember both that there is no placement delete call syntax, and
that old MFC had a bug where placement new was defined without definining
a corresponding placement delete, resulting in a memory leak in debug
builds.
Checking... Oh yes, if the constructor throws during a placement new call
then placement delete is called with the same "placement" args to
deallocate, and this was where the MFC bug was -- so you also need


void operator delete( void*, myFunAllocator )
{
// Used automatically when the constructor throws, §5.3.4/20.
}


as well as

Ok. And what about classes that are NOT derived from MFC, so that you are
sure that their constructor does not throw? (again, I share with you the
point that the Right Thing is to feed also the overload new operator).

The compiler should generate exactly the same code if i say

new(builder) thing;

with thing without a new overloading, but a global overload for "builder"
AND

new thing;

with new overloaded in thing and using "builder" in the overloaded body;

The fact that I am concerned about that is because I may want to change the
builder object in the "new" operator invocations; if I did it by I.e.
overloading the new operator as a static instance in "thing",this second
solution would immediately become less elegant (and even risky) by far than
the global overload by class type and the explicit new(object) call. So, as
this two solution are sintactically different but semantically equivalent,
I am concerned about the compiler generating the same or compatible code.

Giancarlo.
 
G

Giancarlo Niccolai

Tobias Güntner wrote:

// 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*);

<rip>

It is exactly the solution I found before today, and it gives exactly the
problem I reported. As you can see, template expansion of the ~T() call
will call the destructor associated with the class of the POINTER you pass
to CallDestructor(), and not the class of the OBJECT you pass it. So (using
template<class T> void CallDestructor( T*p) instead of void * helps the
compiler a little):

class B: public A {
...
};

A* obj = new(mem) B;
B* obj1 = new(mem) B;

....
CallDestructor( obj ); // resolves in template A, calling p->~A;
CallDestructor( obj1 ); // resolves in class B

or, maintaining the void * as parameter,
CallDestructor< ?what here? you have do decide at compile time>( obj );

and what is in obj, if it can dynamically change at runtime?

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.

The functor is a good solution, but then you don't call the destructor
anymore. This means that stack-allocated object cannot be automatically
unwinded by the compiler. You have to allocate all the items in the heap,
as a smart pointer would not be able to tell a heap-allocated item from a
stack allocated one, while the overloading of the delete operator is.
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 ;) )

Uhm; first of all, you may not want all the objects in your application to
be virtual. For really high-speed aps, as the vtable and the objects are
unlikely to be kept near in memory, the memory cache flushes to access the
vtable and then the object repeatedly may cause an unacceptable operational
downgrade. Secondly, it does not cure the problem, as the problem is
EXACTLY that of being unable to virtualize the destructor due to a lack in
C++ grammar definition: you can call every method by its object-specific
representation EXCEPT for the destructor, which you may call only with its
CLASS specific representation. You may call a->method(), and method is
relative to a object, so the compiler has to find a way to address it: is
it a vtable entry? is it an inline method? what kind of pointer is A? can
it hold virtual objects?. But with destructors, you may call only
a->~CLASSNAME(); and then you reference the classname explicitly, the
compiler won't try to understand what A may be except for that class
instance. The only way to call a destructor by object instance specific
representation is to use delete, but yet delete does not only call the
destructor, but also free the memory... that's the lock-in situation I was
facing; overloading delete seems the only possible solution, and actually,
it worked, but then WHY, is my question, WHY this possibility is explicitly
excluded by the literature about the placed new() operator?

Giancarlo.
 
M

Max M.

Giancarlo said:
Actually, it doesn't, as ~A() semantically means exactly "call the method
named ~A(), which is accidentally A class destructor". As B() class
destructor is called ~B(), and not ~A(), even if they share the same
Vtable entry, they have actually two different symbolic name.

Not sure we're understanding each other. I meant to say I find GCC's
behaviour to be more logical than that of your compiler, not the other way
around. At any rate, as Alf P. Steinbach just pointed out, the Standard is
clear on this. GCC is conformant in this respect; your compiler is not.
See 12.4.12:

[Example:
struct B {
virtual ~B() { }
};
struct D : B {
~D() { }
};
D D_object;
typedef B B_alias;
B* B_ptr = &D_object;
D_object.B::~B(); // calls B's destructor
B_ptr->~B(); // calls D's destructor
B_ptr->~B_alias(); // calls D's destructor


Max
 
G

Giancarlo Niccolai

Rob said:
Why ? / Who says this ?

because ~A() reference a CLASS NAME SYMBOL, and not a method name. So, the
compiler know you are willing to call ~A(), even if ~A() is virtual and
there is a ~B() overloading ~A(). With this construct, the language writers
are assuming that the programmer know before compile time the class from
which the object pointed by mem is instantiated from.
Note Standard C++ has no concept of "Vtable", this is important as the
symantics of virtual destructors (or member-functions or base-classes)
are *not* defined in terms of virtual tables.

Correct: I expressed myself in terms of implementations rather than in terms
of abstraction; but the fact is that vtables implement virtuality in a 1:1
semantic mapping. Virtuality is perfectly represented by vtables, which are
the perfect structure to represent virtuality, that's why I feel confident
in using vtables as a term to specify the compiler view of the virtuality
definitions.
int main()
{
char good_memory[100];
A *mem = new ((void *)good_memory) B();

mem->~A();
}

On Every compiler I tried (*):

output:
~B()
~A()

GCC fails in that. Then is a GCC Issue? Or is a MSVC issue, as it calls ~B()
even when the programmer explicitly calls ~A(). If I call mem->A::method(),
I expect the method from A to be called, even if it's overloaded; calling
the ~() method from class A is not semantically equivalent to call
mem->A::method()?

Giancarlo.
 
G

Giancarlo Niccolai

Alf said:
* Rob Williscroft:

$12.4/12 lists just about the same example and requires the output
given above, so it is mandated by the Holy Standard (one interesting
tidbit, given "typedef B B_alias;" the call b_ptr->~B_alias() is
valid while b_ptr->B_alias::~B_alias() is invalid...).

I didn't know that you get a virtual call here (I don't use this), but
still recommend using member operator new and delete instead of direct
placement new, mainly because a class should encapsulate correct usage.

Ahh, here is the point: $12.4/12. So ~A() is a special case where using the
class name is NOT semantically equivalent to access the class instance by
name (see my other letters). So, I suppose that the problem lies in GCC,
that fails in cope with this standard.

That is then the reason why
mem->~A();
freeFunc(mem);

is the *recommended* procedure to destroy an item allocated with mem(p); if
~A() is semantically treated not as a class name symbol reference, but as a
possibly virtual object method, then all is clear.

Thanks all for your kind explanations.

Giancarlo.
 
G

Giancarlo Niccolai

Max said:
Giancarlo said:
Actually, it doesn't, as ~A() semantically means exactly "call the method
named ~A(), which is accidentally A class destructor". As B() class
destructor is called ~B(), and not ~A(), even if they share the same
Vtable entry, they have actually two different symbolic name.

Not sure we're understanding each other. I meant to say I find GCC's
behaviour to be more logical than that of your compiler, not the other way
around. At any rate, as Alf P. Steinbach just pointed out, the Standard is
clear on this. GCC is conformant in this respect; your compiler is not.
See 12.4.12:

[Example:
struct B {
virtual ~B() { }
};
struct D : B {
~D() { }
};
D D_object;
typedef B B_alias;
B* B_ptr = &D_object;
D_object.B::~B(); // calls B's destructor
B_ptr->~B(); // calls D's destructor
B_ptr->~B_alias(); // calls D's destructor


Max

Ok, Then the problem is that GCC fails if:

template <class C>
func( C *p ) {
.....
p->~C();
}

As you can see, GCC should do as

B_ptr->~B(); // calls D's destructor

but it doesn't. Eventually, then it seems to be a GCC bug in call of
destructor from templates, in which it acts as if:

template <class C>
func( C *p ) {
.....
p->C::~C();
}

Instead of the other way (using virtual destructors).

Giancarlo.
 
K

Kai-Uwe Bux

Giancarlo said:
Ok, Then the problem is that GCC fails if:

template <class C>
func( C *p ) {
....
p->~C();
}

As you can see, GCC should do as

B_ptr->~B(); // calls D's destructor

but it doesn't. Eventually, then it seems to be a GCC bug in call of
destructor from templates, in which it acts as if:

template <class C>
func( C *p ) {
....
p->C::~C();
}

Instead of the other way (using virtual destructors).

Giancarlo.


Now, how did you get that idea:

#include <iostream>

class Base {
public:

int x;

virtual ~Base ( void ) {
std::cout << "destructing Base" << std::endl;
}

};

class Derived : public Base {
public:

int y;

virtual ~Derived ( void ) {
std::cout << "destructing Derived" << std::endl;
}

};


template <class C>
void func( C *p ) {
p->~C();
}

int main ( void ) {

Base* bp = new Derived();
func( bp );

}


Output [ compiled with g++ (GCC) 3.4.0 ] :

destructing Derived
destructing Base



Best

Kai-Uwe Bux
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top