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 23 4193
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
* 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?
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.
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
* 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?
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
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
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/
* 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?
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.
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.
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.
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.
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.
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
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.
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.
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/
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
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 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
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.
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 This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
by: qazmlp |
last post by:
My class in a header file, contains inline virtual destructor.
Is this Ok? Can it cause any problems?
class base
{
public:
base() { }
virtual...
|
by: tuvok |
last post by:
Is it correct that the virtual dtor of base gets called implicitly?
Here's some code to demonstrate what I mean:
Class B has a virtual destructor,...
|
by: Oleksii |
last post by:
Hello,
I'm rather new to the advanced topics, therefore I cannot explain the
following myself. Could anyone give me a hint on this one?
I'm...
|
by: WittyGuy |
last post by:
Hi,
I wonder the necessity of constructor and destructor in a Abstract
Class? Is it really needed?
?
Wg
...
|
by: daniel |
last post by:
I wonder if there is any reasonable reason to make virtual destructor
protected within a derived class purposely?
|
by: Gerhard Prilmeier |
last post by:
Hello,
I have an unmanaged C++ API that uses virtual functions, like this:
class A
{
public:
virtual void handleMe(){}
};
|
by: Dave Rahardja |
last post by:
Since C++ is missing the "interface" concept present in Java, I've been using
the following pattern to simulate its behavior:
class Interface0...
|
by: subramanian100in |
last post by:
Suppose
class Base
{
public:
virtual ~Test() { ... }
// ...
};
class Derived : public Base
|
by: tammygombez |
last post by:
Hey fellow JavaFX developers,
I'm currently working on a project that involves using a ComboBox in JavaFX, and I've run into a bit of an issue....
|
by: tammygombez |
last post by:
Hey everyone!
I've been researching gaming laptops lately, and I must say, they can get pretty expensive. However, I've come across some great...
|
by: concettolabs |
last post by:
In today's business world, businesses are increasingly turning to PowerApps to develop custom business applications. PowerApps is a powerful tool...
|
by: CD Tom |
last post by:
This happens in runtime 2013 and 2016. When a report is run and then closed a toolbar shows up and the only way to get it to go away is to right...
|
by: CD Tom |
last post by:
This only shows up in access runtime. When a user select a report from my report menu when they close the report they get a menu I've called Add-ins...
|
by: Naresh1 |
last post by:
What is WebLogic Admin Training?
WebLogic Admin Training is a specialized program designed to equip individuals with the skills and knowledge...
|
by: jalbright99669 |
last post by:
Am having a bit of a time with URL Rewrite. I need to incorporate http to https redirect with a reverse proxy. I have the URL Rewrite rules made...
|
by: antdb |
last post by:
Ⅰ. Advantage of AntDB: hyper-convergence + streaming processing engine
In the overall architecture, a new "hyper-convergence" concept was...
|
by: Matthew3360 |
last post by:
Hi there. I have been struggling to find out how to use a variable as my location in my header redirect function.
Here is my code.
...
| |