468,771 Members | 1,939 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 468,771 developers. It's quick & easy.

Explicit destructor calls from inside base class destructor

frs
For memory economization, I need to get rid if the virtual
destructor. Under this constraint I get caught up in a design,
where I need to call a destructor of a derived class from a
base class. Briefly it could be displayed like the code below.
It ends up deleting the member 'x' of 'B' twice. Why? Is there
a way around?

Thanks

Frank

================================================== ===================
#include<iostream>
using namespace std;

struct TypeX { ~TypeX() { cerr << "destructed\n"; }
struct A { ~A(); }
struct B : public A {
TypeX x;
~B();
}

A::~A() {
// at this point, ~A knows, that 'this' points is in fact to a 'B'
// (it does! no need to say 'type-switches are bad' - there's no
other way)
delete &(static_cast<B*>(this)->x);
}

int
main(int, char**)
{
A* a = new B;
cerr << "before\n";
// somewhere, there might be someone deleting 'a'
delete a;
cerr << "after\n";
}

Sep 18 '05 #1
20 2421
* frs:
For memory economization, I need to get rid if the virtual
destructor. Under this constraint I get caught up in a design,
where I need to call a destructor of a derived class from a
base class.


Already you have told enough.

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?
Sep 18 '05 #2
frs wrote:
For memory economization, I need to get rid if the virtual
destructor. Under this constraint I get caught up in a design,
where I need to call a destructor of a derived class from a
base class. Briefly it could be displayed like the code below.
It ends up deleting the member 'x' of 'B' twice. Why? Is there
a way around?

Thanks

Frank

================================================== ===================
#include<iostream>
using namespace std;

struct TypeX { ~TypeX() { cerr << "destructed\n"; }
struct A { ~A(); }
struct B : public A {
TypeX x;
~B();
}

A::~A() {
// at this point, ~A knows, that 'this' points is in fact to a 'B'
// (it does! no need to say 'type-switches are bad' - there's no
other way)
delete &(static_cast<B*>(this)->x);


ITYM

static_cast<B*>(this)->x::~TypeX();

but really, I think you should choose a different language to C++ if you
memory constratints are as titght as you say they are. Perhaps you would
be better of with C.

john
Sep 18 '05 #3

frs wrote:
For memory economization, I need to get rid if the virtual
destructor. Under this constraint I get caught up in a design,
where I need to call a destructor of a derived class from a
base class. Briefly it could be displayed like the code below.
It ends up deleting the member 'x' of 'B' twice. Why? Is there
a way around?

Thanks

Frank

================================================== ===================
#include<iostream>
using namespace std;

struct TypeX { ~TypeX() { cerr << "destructed\n"; }
struct A { ~A(); }
struct B : public A {
TypeX x;
~B();
}

A::~A() {
// at this point, ~A knows, that 'this' points is in fact to a 'B'
// (it does! no need to say 'type-switches are bad' - there's no
other way)
delete &(static_cast<B*>(this)->x);
}

int
main(int, char**)
{
A* a = new B;
cerr << "before\n";
// somewhere, there might be someone deleting 'a'
delete a;
cerr << "after\n";
}


You can avoid using a virtual destructor if you use the following smart
pointer (cow_ptr) class:
http://code.axter.com/cow_ptr.h

However, IMHO, you're not going to save much by removing the virtual
destructor.

Sep 18 '05 #4
frs wrote:
For memory economization, I need to get rid if the virtual
destructor. Under this constraint I get caught up in a design,
where I need to call a destructor of a derived class from a
base class. Briefly it could be displayed like the code below.
It ends up deleting the member 'x' of 'B' twice. Why?
The destructor of B calls the destructors of all base classes to take care
of the corresponding subobjects. Thus, the destructor of A is called again.
I would be inclined to assume that this invokes undefined behavior. Anyway,
the following code shows an infinite loop on my machine:

#include <iostream>

struct A {
~A();
};

struct B : public A {

~B ( void ) {
std::cout << "desctructing B\n";
}

};

A::~A ( void ) {
std::cout << "desctructing A\n";
static_cast<B*>( this )->~B();
}

int main ( void ) {
B b;
}

Is there a way around?


Maybe. How is anybody to tell? You did not offer that much of information
regarding what you actually want to accomplish. An item of particular
interest would be: how do you know that objects of type A* are dynamically
of type B*; and if you know that, do you really need runtime polymorphism?
[code snipped]
Best

Kai-Uwe Bux
Sep 18 '05 #5
John Harrison wrote:
frs wrote:
For memory economization, I need to get rid if the virtual
destructor. Under this constraint I get caught up in a design,
where I need to call a destructor of a derived class from a
base class. Briefly it could be displayed like the code below.
It ends up deleting the member 'x' of 'B' twice. Why? Is there
a way around?

Thanks

Frank

================================================== ===================
#include<iostream>
using namespace std;

struct TypeX { ~TypeX() { cerr << "destructed\n"; }
struct A { ~A(); }
struct B : public A {
TypeX x;
~B();
}

A::~A() {
// at this point, ~A knows, that 'this' points is in fact to a 'B'
// (it does! no need to say 'type-switches are bad' - there's no
other way)
delete &(static_cast<B*>(this)->x);

ITYM

static_cast<B*>(this)->x::~TypeX();


Soory, didn't check that the above compiled. Here's what I really meant.

#include<iostream>
using namespace std;

struct TypeX { ~TypeX() { cerr << "destructed\n"; } };
struct A { ~A(); };
struct B : public A {
TypeX x;
~B();
};

A::~A() {
static_cast<B*>(this)->x.~TypeX();
}

int
main(int, char**)
{
A* a = new B;
cerr << "before\n";
// somewhere, there might be someone deleting 'a'
delete a;
cerr << "after\n";
}

output is

before
destructed
after

John
Sep 18 '05 #6

"frs" <fr**************@gmx.net> wrote in message
news:11**********************@g49g2000cwa.googlegr oups.com...
For memory economization, I need to get rid if the virtual
destructor. Under this constraint I get caught up in a design,
where I need to call a destructor of a derived class from a
base class. Briefly it could be displayed like the code below.
It ends up deleting the member 'x' of 'B' twice. Why? Is there
a way around?

Thanks

Frank


From stepping through the code first you are deleting x explicitly then the
destructor of A also deletes it implicitly.

i.e. your problem seems to act as if you did the following

main()
{
TypeX x;

delete &x;

}

Try using a pointer to TypeX and see if it works better or not.

i.e.

TypeX* x;

and change

delete &(static_cast<B*>(this)->x); to

delete (static_cast<B*>(this)->x);

What seems to be the problem as I pointed out earlier is that you delete a
non-pointer and then the destructor deletes it too... it doesn't know that
you already deleted it and probably because the method doesn't seem very
"stable" in the sense its kinda forcing the language to do something that
doesn't seem natural(IMO). Maybe you should rething the design and see if
you can't make it more natural? Maybe you can override new and delete for
your needs?

Jon
Sep 18 '05 #7
frs wrote:
For memory economization, I need to get rid if the virtual
destructor.
Does a virtual destructor really increase your memory needs so much? Which
platform are you writing that code for, if a few bytes of memory are too
much?
Under this constraint I get caught up in a design,
where I need to call a destructor of a derived class from a
base class. Briefly it could be displayed like the code below.
It ends up deleting the member 'x' of 'B' twice. Why?
Because when the end of an object's life is reached, the destructor gets
called automatically.
Is there a way around?
Well, don't use polymorphism if the (actually quite small) overhead is too
much for your platform.
================================================== ===================
#include<iostream>
using namespace std;

struct TypeX { ~TypeX() { cerr << "destructed\n"; }
struct A { ~A(); }
struct B : public A {
TypeX x;
~B();
}

A::~A() {
// at this point, ~A knows, that 'this' points is in fact to a 'B'
// (it does! no need to say 'type-switches are bad' - there's no
other way)
If at this place, the object can only be a B, why would A need to care for
the destruction? Why would you need polymorphism at all?
delete &(static_cast<B*>(this)->x);
You must never give a pointer to delete that you didn't get from new (except
a null pointer).
}

int
main(int, char**)
{
A* a = new B;
Why do you use an A*? Above, you say that all objects are of type B. So just
use a B*.
cerr << "before\n";
// somewhere, there might be someone deleting 'a'
delete a;
cerr << "after\n";
}


Sep 18 '05 #8
"frs" <fr**************@gmx.net> wrote in message
news:11**********************@g49g2000cwa.googlegr oups.com...
For memory economization, I need to get rid if the virtual
destructor.
You can in fact gain a miniscule amount of time by making the destructor
non-virtual, a time which probably doesn't make the application fast; but
you cannot make the application use less memory by doing that.

Actually, you may be able to shave off one function pointer from the
application :)
Under this constraint I get caught up in a design,
where I need to call a destructor of a derived class from a
base class.
You can't call the destructor of the derived class from the base class,
because the derived object doesn't exist anymore at the time the base class
destructor is called.

When the destructor of the base class is called, all of the parts of the
derived are already dead. You can't call the derived object's destructor and
expect meaningful things to happen.
struct TypeX { ~TypeX() { cerr << "destructed\n"; }
struct A { ~A(); }
struct B : public A {
TypeX x;
~B();
}

A::~A() {
// at this point, ~A knows, that 'this' points is in fact to a 'B'
// (it does! no need to say 'type-switches are bad' - there's no
other way)
Your exclamation mark above suggests that you don't completely understand
object lifetimes. In the destructor above, this points to an A and an A
alone. There is no B left at that time.
delete &(static_cast<B*>(this)->x);


Undefined behavior. If ~A is being called through the destruction of a B
object, the destructor of B is already called and is about to complete. You
cannot call it again.

Ali
Sep 18 '05 #9
John Harrison wrote:
John Harrison wrote:
frs wrote:
For memory economization, I need to get rid if the virtual
destructor. Under this constraint I get caught up in a design,
where I need to call a destructor of a derived class from a
base class. Briefly it could be displayed like the code below.
It ends up deleting the member 'x' of 'B' twice. Why? Is there
a way around?


Soory, didn't check that the above compiled. Here's what I really meant.

#include<iostream>
using namespace std;

struct TypeX { ~TypeX() { cerr << "destructed\n"; } };
struct A { ~A(); };
struct B : public A {
TypeX x;
~B();
};

A::~A() {
static_cast<B*>(this)->x.~TypeX();
}


This rather avoids the issue; the B's destructor is never called
(and the memory for the B is never released).

Since the OP does not wish to use polymorphishm, maybe he could
make an A member variable of B, rather than using inheritance.
Then the destructor will work correctly.

Sep 19 '05 #10
Old Wolf wrote:
John Harrison wrote:
John Harrison wrote:
frs wrote:
For memory economization, I need to get rid if the virtual
destructor. Under this constraint I get caught up in a design,
where I need to call a destructor of a derived class from a
base class. Briefly it could be displayed like the code below.
It ends up deleting the member 'x' of 'B' twice. Why? Is there
a way around?


Soory, didn't check that the above compiled. Here's what I really meant.

#include<iostream>
using namespace std;

struct TypeX { ~TypeX() { cerr << "destructed\n"; } };
struct A { ~A(); };
struct B : public A {
TypeX x;
~B();
};

A::~A() {
static_cast<B*>(this)->x.~TypeX();
}

This rather avoids the issue; the B's destructor is never called
(and the memory for the B is never released).

Since the OP does not wish to use polymorphishm, maybe he could
make an A member variable of B, rather than using inheritance.
Then the destructor will work correctly.


Of course:

void f()
{
A a;
}

leads to UB.
Sep 19 '05 #11
Old Wolf wrote:
John Harrison wrote:
John Harrison wrote:
frs wrote:
For memory economization, I need to get rid if the virtual
destructor. Under this constraint I get caught up in a design,
where I need to call a destructor of a derived class from a
base class. Briefly it could be displayed like the code below.
It ends up deleting the member 'x' of 'B' twice. Why? Is there
a way around?


Soory, didn't check that the above compiled. Here's what I really meant.

#include<iostream>
using namespace std;

struct TypeX { ~TypeX() { cerr << "destructed\n"; } };
struct A { ~A(); };
struct B : public A {
TypeX x;
~B();
};

A::~A() {
static_cast<B*>(this)->x.~TypeX();
}

This rather avoids the issue; the B's destructor is never called
(and the memory for the B is never released).


Well I think that's the point and whether the memory is released or not
depends on the OP's implementation of delete.

I'm not advising the above code, it may well be UB for all I know. I'm
just trying to answer the OP's question.

john
Sep 19 '05 #12
frs
If I am not totally mistaken, then

delete a;

does not call the (non-virtual) destructor of class B, because 'a'
was defined as "A* a = new B;".

So the destructor of B is never called.

Sep 19 '05 #13

"frs" <fr**************@gmx.net> wrote in message
news:11**********************@g49g2000cwa.googlegr oups.com...
If I am not totally mistaken, then

delete a;

does not call the (non-virtual) destructor of class B, because 'a'
was defined as "A* a = new B;".

So the destructor of B is never called.


Yes, stepping through the code showed that only the destructor of TypeX and
A was called... Though is it really the destructor of A or the destructor of
B? In any case it seems that delete is being called twice on the same
object, once by the explicit delete in A's constructor and once by an
implicit delete in A's destructor(atleast this is what stepping through the
code shows). I don't know why A's destructor would try to delete x but it
does. Its as if A's destructor is really B. I don't know though. All I do
know is that you should never delete something that wasn't allocated with
new.

Jon
Sep 19 '05 #14
frs
Your last phrase, probably hits the point. I guess that x was somehow
allocated in the frame of the 'newish' 'B' allocation. Deleting x would
not
ensure that everything is cleaned up propperly, even though it is
the only member of B.
From the responses I got, I guess, this problem is a tough one. I kept

the virtual destructor and labeled the issue as 'premature
optimization.'
Before, I have a clear idea about the behind the scene handling of
new and delete, this design is way to daring.

thanks anyway.

Sep 20 '05 #15
frs
The code strip is a heavily cut down of reality, of course.
'B' is one of many derived classes of A. 'A' knows about 'B' though
some sort of 'type_id' (category id).

The content of those classes does not exceed a few bytes themselves.
Thus, if I have thousands of those little objects, memory increases
just by the existance of virtual destructors by 30% to 50 %.

Sep 20 '05 #16

"frs" <fr**************@gmx.net> wrote in message
news:11**********************@o13g2000cwo.googlegr oups.com...
The code strip is a heavily cut down of reality, of course.
'B' is one of many derived classes of A. 'A' knows about 'B' though
some sort of 'type_id' (category id).

The content of those classes does not exceed a few bytes themselves.
Thus, if I have thousands of those little objects, memory increases
just by the existance of virtual destructors by 30% to 50 %.


There is a class that you can use to handle small object allocation by the
guy who wrote Modern C++ design. In the book he also teaches you how to
create the class and if you read it you might be able to modify/use it for
your purpose.

Jon
Sep 20 '05 #17
frs wrote:
The code strip is a heavily cut down of reality, of course.
'B' is one of many derived classes of A. 'A' knows about 'B' though
some sort of 'type_id' (category id).
Why don't you want to use C++'s built-in polymorphism instead of some
home-grown solution?
The content of those classes does not exceed a few bytes themselves.
Thus, if I have thousands of those little objects, memory increases
just by the existance of virtual destructors by 30% to 50 %.


A virtual destructor adds one pointer per class, not per object. Or is the
destructor the only virtual member?
On most - if not all - implementations, making a class polymorphic (i.e.
giving it virtual member functions) will add the size of one pointer per
object. If you add more virtual member funcitons, the object size will not
increase further. It only costs the size of one function pointer per class
and function.

Sep 20 '05 #18
frs
Thanks,

but I was was indeed looking for avoidance of virtual functions at all.
So,
no pointer to a virtual function table would be necessary.

Sep 21 '05 #19
frs wrote:
Thanks,

but I was was indeed looking for avoidance of virtual functions at all.
So, no pointer to a virtual function table would be necessary.


I see. However, if you implement your own replacement for them, you need to
store type information too. The code is likely to be slower than the
virtual functions in C++, so you should be sure that the additional work
actually pays off.

Sep 21 '05 #20
* frs:

but I was was indeed looking for avoidance of virtual functions at all.


Re-design so that you avoid polymorphic delete; that's _all_ you need to do,
and you need to do that anyway if you don't have any virtual functions.

Forget about taking control. You don't need more detailed control. You need
instead to understand that the built-in mechanism in C++ is just about the
only one that makes sense, and that the goal is therefore to work with the
langauge so as to use that built-in mechanism, not to replace it (which will
be less efficient) or "improve" it (which will be incorrect and possibly UB).

Having said that, however, it may be that you need a few ideas for the
re-design. And one such idea that I think you'll benefit from is the
flyweight pattern. It tackles the problem of having many small objects.

--
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?
Sep 21 '05 #21

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

20 posts views Thread by qazmlp | last post: by
11 posts views Thread by santosh | last post: by
5 posts views Thread by Frederick Gotham | last post: by
23 posts views Thread by Ben Voigt | last post: by
4 posts views Thread by Subra | last post: by
12 posts views Thread by Rahul | last post: by
6 posts views Thread by Pallav singh | last post: by
1 post views Thread by Marin | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.