virtual destructors for classes only with virtual functions?

R

Richard Herring

E. Robert Tisdale said:
This is generally bad advice.

It's generally safe advice.
Ignore it.

Ignore it when you can be certain of the consequences.
Don't define a virtual destructor unless you have a specific use for it.

.... such as the common operation of deleting polymorphic objects via a
pointer to base.
Generally, C++ functions shouldn't be destroying objects passed to them
by reference (or by reference through a pointer).

Non sequitur. *Something* has to destroy objects created by new, and if
the objects are being used polymorphically, that something probably
doesn't know the dynamic type of the thing pointed to.
 
E

E. Robert Tisdale

Peter said:
When you create a class with a virtual function,
by definition you are going to use that class in a polymorphic way.

I have no idea what you mean by that.
In that case, it is reasonable to expect some user of that class
to delete an object derived from that class
by calling the destructor with only access to your base-class.
You might not anticipate any such need
but having a virtual destructor enables that feature.

Generally, it's a bad idea to have functions delete objects
passed to them by reference (or by reference through a pointer):
cat main.c++
#include <iostream>

class X {
private:
// representation
int I;
public:
// operators
friend
std::eek:stream& operator<<(std::eek:stream& os, const X& x) {
return os << x.I; }
// constructors
X(int i = 0): I(i) { }
virtual
~X(void) { }
};

void f(const X* p) {
delete p;
}

int main(int argc, char* argv[]) {
X x(13);
f(&x);
std::cout << "x = " << x << std::endl;
return 0;
}
So let's have a look at the cost. A virtual destructor is expensive
in a class that does not already have virtual functions:
the vtable entry incurs an extra cost for every object created.
If you already have a virtual function this cost disappears
so the added storage cost becomes practically zero.
As for runtime costs, there should not be any overhead either.
The compiler will not have to call the destructor using the vtable
when the type of the object is statically known.

Have you actually tested this?
cat f.c++
#include <iostream>

class X {
private:
// representation
int I;
public:
// operators
friend
std::eek:stream& operator<<(std::eek:stream& os, const X& x) {
return os << x.I; }
// constructors
X(int i = 0): I(i) { }
virtual
~X(void) { }
};

void f(const X* p) {
for (size_t j = 0; j < 1000000000; ++j) {
X* p = new X;
delete p;
}
}
 
P

Peter Koch Larsen

Pete Becker said:
If you read my post again, you will see that your argument does not hold
water. I specifically said that the reason for having a virtual destructor
is that your design calls for deleting objects of derived types through
pointers to the base. That is not the same as having or not having other
virtual functions, nor does it have anything to do with being "used
polymorhpically."

Surely we agree on that item: virtual destructors are for deleting derived
objects via a base-pointer, and not anything else. Still, if i saw code with
a virtual function but no virtual destructor i would be suspicious. I have
yet to see a class where there has been a virtual function without a virtual
destructor or some means for prohibiting a direct "new".

/Peter
 
P

Peter Koch Larsen

E. Robert Tisdale said:
I have no idea what you mean by that.
Isn't the only reason for a virtual function that the object is intended to
be derived from and to be used polymorphically?
Generally, it's a bad idea to have functions delete objects
passed to them by reference (or by reference through a pointer):

I do not know how the object is to be deleted. Someone did new it (since my
advice to "hide" operator new wasn't followed), and thus it should be
deleted somehow. If it is via a plain delete or some smart pointer, I don't
know, but since it was newed, I assume it can not happen at the place of
construction (no reason to use new).

[snip]
So let's have a look at the cost. A virtual destructor is expensive
in a class that does not already have virtual functions:
the vtable entry incurs an extra cost for every object created.
If you already have a virtual function this cost disappears
so the added storage cost becomes practically zero.
As for runtime costs, there should not be any overhead either.
The compiler will not have to call the destructor using the vtable
when the type of the object is statically known.

Have you actually tested this?
[snop example]

How do I get my GNU C++ compiler to optimize away
the virtual destructor call in this case?
I don't know. What I know is that a static call to the known destructor
would be fine in the example you gave. And if you read my original post, you
will notice that if the call of the virtual destructor turns out to be to
expensive, I see apn excuse to remove the destructor in that particular
case. This is an inconvenience for the user of the class however, and i
recommend instead that the new operator should be made private (come to
think of it, removing "delete" is far superior as it only requires a
"destroy" function).
It doesn't make any sense to make a destructor a virtual function
unless you actually write code that *must* use it that way.

I see it this way:
It doesn't make any sense to make a destructor a virtual function
unless you actually write code that *might* use it that way. I call it
"defensive" programming.
If someone else needs a virtual destructor,
they can always derive a new base class from your base class
that *has* a virtual destructor
and use it to define their new functions instead. They sure can.

/Peter
 
P

Pete Becker

Peter said:
I have
yet to see a class where there has been a virtual function without a virtual
destructor or some means for prohibiting a direct "new".

"There are more things in heaven and Earth, Horatio, than are dreamt of
in your philosophy."
 
E

E. Robert Tisdale

Peter said:
Isn't the only reason for a virtual function that
the object is intended to be derived from and to be used polymorphically?

Are you claiming that
an object which does not "have" a virtual destructor
cannot be used polymorphically?
If so, you really need to elaborate on your notion of polymorphism.
I do not know how the object is to be deleted.
Someone did new it (since my advice to "hide" operator new wasn't followed)
and thus it should be deleted somehow.
If it is via a plain delete or some smart pointer,
I don't know but, since it was new'd,
I assume it can not happen at the place of construction (no reason to use new).

I have no idea what you are talking about here.
Evidently, you are *not* talking about my example:

void f(const X* p) {
delete p;
}

int main(int argc, char* argv[]) {
X x(13);
f(&x);
std::cout << "x = " << x << std::endl;
return 0;
}

which you snipped.

Maybe you could provide an example which shows what you mean.
Have you actually tested this?

[snip example]
How do I get my GNU C++ compiler to optimize away
the virtual destructor call in this case?

I don't know. What I know is that a static call to the known destructor
would be fine in the example you gave.
And, if you read my original post, you will notice that,
if the call of the virtual destructor turns out to be to expensive,
I see an excuse to remove the destructor in that particular case.
This is an inconvenience for the user of the class however
and I recommend instead that the new operator should be made private
(come to think of it, removing "delete" is far superior
as it only requires a "destroy" function).
It doesn't make any sense to make a destructor a virtual function
unless you actually write code that *must* use it that way.

I see it this way:
It doesn't make any sense to make a destructor a virtual function
unless you actually write code that *might* use it that way.

Can you give us an example of code
that *needs* to use a virtual destructor?
I call it "defensive" programming.


They sure can.

When superstitious people spill salt,
they still throw a pinch over their shoulder to ward off bad luck.
I'm not sure how the practice got started but I'm pretty sure that
this virtual destructor thing is a modern example.
You suggest a hypothetical case
where "someone else needs a virtual destructor"
but you haven't shown us an example of such a case.
Situations where you might employ a virtual destructor
seem to require a "virtual constructor" as well.

You main argument for virtual destructors seems to be that
they don't hurt very much.
 
C

cadull

Surely we agree on that item: virtual destructors are for deleting derived
objects via a base-pointer, and not anything else. Still, if i saw code
with a virtual function but no virtual destructor i would be suspicious. I
have yet to see a class where there has been a virtual function without a
virtual destructor or some means for prohibiting a direct "new".

/Peter

I just happen to be working on such a class now. It's simply an abstract
base class providing an interface so I can store a list of derived objects
and call a few functions on them. I may even add some data members if
necessary.

class IVolatileResource
{
public:
virtual void OnResourceLost()=0;
virtual void OnResourceRestore()=0;
};

No-one is ever intended, nor do I predict a use for, deleting via an
IVolatileResource*. Yet now all classes implementing this interface have
virtual functions. Should they all now have a virtual destructor? Should
IVolatileResource have one? IMO they don't (the derived classes may for some
other reason). Alternatively, should 'new' be disabled? What is
IVolatileResource to dictate such terms to derived classes?

Anyway, just thought I'd join in with an example of where I consider virtual
destructors unnecessary. For a base type that provides more useful
functionality and thus more likely to be kept as that type I would agree and
suggest that a virtual destructor be seriously considered.

Regard,
cadull
 
S

Stephen Howe

Still, if i saw code with a virtual function but no virtual destructor i
would be suspicious.

You are mistaken.
What you say is true _ONLY IF_ the class is publically inherited.
The class could be inherited privately (or protectedly) and at that point
there is no need for a virtual destructor.
And these days there are quite few advocates for private inheritance.

Stephen Howe
 
P

Peter Koch Larsen

E. Robert Tisdale said:
Are you claiming that
an object which does not "have" a virtual destructor
cannot be used polymorphically?
If so, you really need to elaborate on your notion of polymorphism.

This thread is a consequence of your original reply:

[original poster]
"A virtual destructor should be defined
if the class contains at least one virtual member function."
[robert e. tisdale]
This is generally bad advice. Ignore it.

So - obviously we are talking about "guidelines" here - not absolutes. In
those terms, polymorphic classes should NORMALLY have a virtual destructor,
and if they do not, you should either hide new and/or delete.

/Peter
 
E

E. Robert Tisdale

Peter said:
So - obviously we are talking about "guidelines" here - not absolutes.
In those terms, polymorphic classes should NORMALLY have a virtual destructor,
and if they do not, you should either hide new and/or delete.

I still don't understand why you need to hide new and/or delete.
Could you elaborate a little?
 
P

Peter Koch Larsen

E. Robert Tisdale said:
I still don't understand why you need to hide new and/or delete.
Could you elaborate a little?

I intended to avoid the scenario

base* b = new derived(...);
delete b; //oops - cant do with non-virtual destructor.

which a careless programmer might do - considering the "polymorphic" nature
of the base-class.
Requiring the user to write b->destroy() or something like that might
wake-up the programmer.
Of course, one could argue that proper documentation is adequate, but if i
were to make the class public (not just for in-house use), it could be
argued that my approach is somewhat more userfriendly.

/Peter
 
P

Pete Becker

Peter said:
it could be
argued that my approach is somewhat more userfriendly.

It could also be argued that it is somewhat more user unfriendly,
because users who know what they're doing have to work around your
obstruction by writing an operator new for their derived class. Don't
try to force me to write code according to your guidelines. You don't
know enough about what I'm doing.
 
E

E. Robert Tisdale

Pete said:
It could also be argued that it is somewhat more user unfriendly,
because users who know what they're doing have to work around your
obstruction by writing an operator new for their derived class.
Don't try to force me to write code according to your guidelines.
You don't know enough about what I'm doing.

I agree. Larsen's approach is miguided.
 
C

Chris Dearlove

Pete Becker ([email protected]) wrote:
: It could also be argued that it is somewhat more user unfriendly,
: because users who know what they're doing have to work around your
: obstruction by writing an operator new for their derived class.

If I have understood correctly, this is in response to Mr. Larsen's
suggestion of making operator new private and providing a destroy()
function. What about if instead you make operator new protected?

(I'm not recommending this, I'm just enquiring.)
 

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

No members online now.

Forum statistics

Threads
474,202
Messages
2,571,057
Members
47,667
Latest member
DaniloB294

Latest Threads

Top