By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
455,434 Members | 1,858 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 455,434 IT Pros & Developers. It's quick & easy.

Virtual dtor and placement new.

P: n/a
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
Jul 22 '05 #1
Share this Question
Share on Google+
23 Replies


P: n/a
Giancarlo Niccolai wrote:
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
Jul 22 '05 #2

P: n/a
* 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.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 22 '05 #3

P: n/a
Alf P. Steinbach wrote:
* Giancarlo Niccolai:

How to use virtual destructors when dealing with placed memory?
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.

Jul 22 '05 #4

P: n/a
Giancarlo Niccolai wrote:
[...]
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
Jul 22 '05 #5

P: n/a
* 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:
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


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.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 22 '05 #6

P: n/a
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/articleco...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
Jul 22 '05 #7

P: n/a
Victor Bazarov wrote:
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.


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





Jul 22 '05 #8

P: n/a
Giancarlo Niccolai wrote in news:fo**********************@news4.tin.it
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.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #9

P: n/a
* 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.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 22 '05 #10

P: n/a
Max M. wrote:
Victor Bazarov wrote:
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.


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.

Jul 22 '05 #11

P: n/a
Victor Bazarov wrote:
Giancarlo Niccolai wrote:
[...]
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.

Jul 22 '05 #12

P: n/a
Alf P. Steinbach wrote:

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.


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
void operator delete( void * ) {
... do something that works with myFunAllocator;
}
};


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.

Jul 22 '05 #13

P: n/a
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.

Jul 22 '05 #14

P: n/a
Rob Williscroft wrote:
// 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 ?


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.

Jul 22 '05 #15

P: n/a
Giancarlo Niccolai wrote:
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?


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
Jul 22 '05 #16

P: n/a
Alf P. Steinbach wrote:
* Rob Williscroft:
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.


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.

Jul 22 '05 #17

P: n/a
Max M. wrote:
Giancarlo Niccolai wrote:
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?


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.

Jul 22 '05 #18

P: n/a
Alf P. Steinbach wrote in news:41****************@news.individual.net in
comp.lang.c++:

struct UDT {};
void operator new ( std::size_t, UDT * )
{
return 0;
}

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


Cast is not necessary.


Yep. Thanks that was nit-ilitious :).

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #19

P: n/a
Giancarlo Niccolai wrote:
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
Jul 22 '05 #20

P: n/a
Giancarlo Niccolai wrote:
template<class T>
void CallDestructor(void* p)
{
static_cast<T*>(p)->~T();
}
typedef void (*DestructPtr)(void*);

<snip>
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
That's not what I had in mind.

A* pA = new(mem) B;
DestructPtr Cleanup_mem = CallDestructor<B>;

// later
Cleanup_mem(mem); // Calls ((B*)mem)->~B();

It would be necessary to store both mem and Cleanup_mem somewhere to
perform the required destructor call. A smart-pointer could be modified
so that it calls Cleanup_pA(mem); instead of delete obj;
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.
So why do you need deletion through a base class then? If you want to
call the derived class' destructor, you need a virtual function call (or
something similar, e.g. pointer to function) anyway.
If you don't want that extra level of indirection, you need to know the
exact type of the object you want to delete - no need to delete through
pointer to base in this case.
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:


As the others have already pointed out, according to the Standard it
should not be necessary, because a call to ~A() automatically results in
~B() (I didn't know that either when I wrote my first post)
--
Regards,
Tobias
Jul 22 '05 #21

P: n/a
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


[gian@head: tests]$ gcc -v
Reading specs from /usr/lib/gcc-lib/i486-slackware-linux/3.2.3/specs
Configured with: ../gcc-3.2.3/configure --prefix=/usr --enable-shared
--enable-threads=posix --enable-__cxa_atexit --disable-checking
--with-gnu-ld --verbose --target=i486-slackware-linux
--host=i486-slackware-linux
Thread model: posix
gcc version 3.2.3
Original code failing is:

template <class _Tp >
inline void destroy( _Tp *p )
{
if ( p != 0 ) {
p->~_Tp();
memFree( p );
}
}

this was failing specifically when trying to destroy a class derived from
std::list (the virtual constructor of the DERIVED hyerarcy not being
properly called). I did not dug to discover if the error was in std::
implementation or in the core compiler; I have now repeated the test you
posted, and it works, so it seem it was in stl implementation.

Bests and thanks

Giancarlo
Jul 22 '05 #22

P: n/a
Tobias Güntner wrote:
So why do you need deletion through a base class then? If you want to
call the derived class' destructor, you need a virtual function call (or
something similar, e.g. pointer to function) anyway.
If you don't want that extra level of indirection, you need to know the
exact type of the object you want to delete - no need to delete through
pointer to base in this case.
The point was being able to use a memory manager through a whole application
without making all the classes virtual, but only those that really needs
it.
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:


As the others have already pointed out, according to the Standard it
should not be necessary, because a call to ~A() automatically results in
~B() (I didn't know that either when I wrote my first post)


Yes now I got it: I just stumbled on some mis-implementation of the standard
(probably, but not sure as now I eliminated the problem, and I am not going
to recreate it, in a implementation of std::list in older GCC ;-) that
drove me mad.

Thanks,
Giancarlo.

Jul 22 '05 #23

P: n/a
Giancarlo Niccolai wrote:
Original code failing is:

template <class _Tp >
inline void destroy( _Tp *p )
{
if ( p != 0 ) {
p->~_Tp();
memFree( p );
}
}
Hm,
I am using a homegrown
template < typename T, typename Allocator > class pointer_to;

instead of raw pointers in my programms. Its deallocation method is
more or less exactly like your code above. It never failed me.
Now, you say: this was failing specifically when trying to destroy a class derived from
std::list (the virtual constructor of the DERIVED hyerarcy not being
properly called).


Note that std::list<> does not have a virtual destructor. Thus,

std::list< SomeClass > *

cannot be used polymorphically. This is the reason that

(a) many fellows on this list strongly advise against deriving from
standard container classes, and

(b) I actually use two templates: pointer_to<T> and polymorphic_ptr<T>. The
first of which does not allow for polymorphism and the second of which
yields compile time errors if the base type T does not have virtual
functions.

Best

Kai-Uwe Bux
Jul 22 '05 #24

This discussion thread is closed

Replies have been disabled for this discussion.