Explicit destructor calls from inside base class destructor

F

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";
}
 
A

Alf P. Steinbach

* 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.
 
J

John Harrison

frs said:
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
 
A

Axter

frs said:
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.
 
K

Kai-Uwe Bux

frs said:
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
 
J

John Harrison

John said:
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
 
J

Jon Slaughter

frs said:
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
 
R

Rolf Magnus

frs said:
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*.
 
G

Guest

frs said:
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
 
O

Old Wolf

John said:
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.
 
R

red floyd

Old said:
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.
 
J

John Harrison

Old said:
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
 
F

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.
 
J

Jon Slaughter

frs said:
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
 
F

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.
 
F

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 %.
 
J

Jon Slaughter

frs said:
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
 
R

Rolf Magnus

frs said:
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.
 
F

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.
 
R

Rolf Magnus

frs said:
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.
 

Ask a Question

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

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

Ask a Question

Members online

Forum statistics

Threads
473,962
Messages
2,570,134
Members
46,690
Latest member
MacGyver

Latest Threads

Top