[comp.lang.c++] Virtual destructors - why aren't they all?

keith@csli.Stanford.EDU (Keith Nishihara) (10/11/89)

I was surprised to discover that calling delete on a derived class
object pointed to by a pointer to the base class failed to call
the derived class destructor. (cfront 1.2) (See example below.)
I cannot think of any case where this would be the desirable behaviour.
Fortunately, it appears that declaring the destructor virtual causes
the appropriate behaviour to happen.  Why are not all desctructors
virtual?  Is this true in other C++ systems?

Example:

class base
{
public:
	base();
	~base() { printf("Base destructor\n); }
private:
	// more stuff.
};

class derived : public base
{
public:
	derived();
	~derived() { printf("Derived destructor.\n"); }
private:
	// more stuff.
};

main()
{
	base *p = new derived;
	delete p;
// "Base destructor"

	derived *q = new derived;
	delete q;
// "Derived destructor"
// "Base destructor"
}

Neil/.

jima@hplsla.HP.COM (Jim Adcock) (10/12/89)

>keith@csli.Stanford.EDU (Keith Nishihara) /  5:55 pm  Oct 10, 1989 /
>I was surprised to discover that calling delete on a derived class
>object pointed to by a pointer to the base class failed to call
>the derived class destructor. (cfront 1.2) (See example below.)
>I cannot think of any case where this would be the desirable behaviour.
>Fortunately, it appears that declaring the destructor virtual causes
>the appropriate behaviour to happen.  Why are not all desctructors
>virtual?  Is this true in other C++ systems?

Well, one answer is that declaring a function virtual causes both a space
penalty in an object of that class [said penalty constant as long as one
or more virtual functions are declared] and also a time penalty in invoking
that virtual function.  Presumably, if someone gives you a class with a 
non-virtual destructor and you'd rather have a virtual destructor, then 
you can trivially derive a new class that has a virtual destructor.  But
you can't go the other way.  Once a superclass declares a virtual function
subclasses always get to eat the space and/or time penalties.

This behavior is true of all C++ I have seen.  If you want a function to
be virtual, declare it virtual.  This requirement also has the advantage of 
language consistency.

shopiro@alice.UUCP (Jonathan Shopiro) (10/15/89)

In article <10627@csli.Stanford.EDU>, keith@csli.Stanford.EDU (Keith Nishihara) writes:
- I was surprised to discover that calling delete on a derived class
- object pointed to by a pointer to the base class failed to call
- the derived class destructor. (cfront 1.2) (See example below.)
- I cannot think of any case where this would be the desirable behaviour.
- Fortunately, it appears that declaring the destructor virtual causes
- the appropriate behaviour to happen.  Why are not all desctructors
- virtual?  Is this true in other C++ systems?

It's true at least in C++ 2.0.  The reason why is that some classes
have no virtual functions, and adding a virtual destructor would cost
one word per object of the class.  Some classes are not intended to be
inherited from, so they don't need any virtual functions.  I think it's
generally true that if a class has any virtual functions, it should
have a virtual destructor, but it's up to you to do it.
-- 
		Jonathan Shopiro
		AT&T Bell Laboratories, Warren, NJ  07060-0908
		research!shopiro   (201) 580-4229

mce@tc.fluke.COM (Brian McElhinney) (10/17/89)

In article <6590296@hplsla.HP.COM> jima@hplsla.HP.COM (Jim Adcock) writes:
>>keith@csli.Stanford.EDU (Keith Nishihara) /  5:55 pm  Oct 10, 1989 /
>>the appropriate behaviour to happen.  Why are not all desctructors
>>virtual?  Is this true in other C++ systems?
>
>Well, one answer is that declaring a function virtual causes both a space
>penalty in an object of that class [said penalty constant as long as one
>or more virtual functions are declared] and also a time penalty in invoking
>that virtual function.

This isn't a requirement of the C++ language definition, is it?  I understood
it to be a result of the cfront implementation (eg, relying on standard C and
its linker).  I would expect more advanced C++ compilers to optimize this case;
if a virtual method is never overloaded, call the method directly.

>This behavior is true of all C++ I have seen.  If you want a function to
>be virtual, declare it virtual.  This requirement also has the advantage of 
>language consistency.

And the disadvantage of requiring the original author to anticipate exactly
which methods I will need to override.  You shouldn't need to change
"resuable" source code in order to reuse it.
 
 
Brian McElhinney		  "Knowledge is soon changed, then lost in the
mce@tc.fluke.com		   mist, an echo half-heard"      --Gene Wolfe

dml@esl.com (Denis Lynch) (10/18/89)

In article <11748@fluke.COM> mce@tc.fluke.COM (Brian McElhinney) writes:
   In article <6590296@hplsla.HP.COM> jima@hplsla.HP.COM (Jim Adcock) writes:
   >>keith@csli.Stanford.EDU (Keith Nishihara) /  5:55 pm  Oct 10, 1989 /
   >>the appropriate behaviour to happen.  Why are not all desctructors
   >>virtual?  Is this true in other C++ systems?
   >
   >Well, one answer is that declaring a function virtual causes both a space
   >penalty in an object of that class [said penalty constant as long as one
   >or more virtual functions are declared] and also a time penalty in invoking
   >that virtual function.

   This isn't a requirement of the C++ language definition, is it?  I understood
   it to be a result of the cfront implementation (eg, relying on standard C and
   its linker).  I would expect more advanced C++ compilers to optimize this case;
   if a virtual method is never overloaded, call the method directly.

   >This behavior is true of all C++ I have seen.  If you want a function to
   >be virtual, declare it virtual.  This requirement also has the advantage of 
   >language consistency.

   And the disadvantage of requiring the original author to anticipate exactly
   which methods I will need to override.  You shouldn't need to change
   "resuable" source code in order to reuse it.

I find this an interesting juxtaposition of arguments, Brian.

First, the "smart compiler" problem. Since reusable code is likely to go in a
library, the compiler can't possibly tell whether an operator is overloaded.
Unless you expect to have access to the source code for all your libraries, which
the library vendors aren't likely to be excited about.

Second, the "anticipation" problem. Here, you must be arguing that you want
*all* member functions to be virtual, i.e. overridable. If that's what you
want, you don't need a smart compiler or a language change, just declare all
your functions virtual.

The current language gives you the option of writing member functions that you
*know* will not be overriden, and others that may be. This is a reasonable
extension of the public/private/protected distinctions: you can have code
that is guaranteed to call the function you wrote, never a client's "better
idea."

Or did I miss something?

Denis Lynch
dml@esl.com

mce@tc.fluke.COM (Brian McElhinney) (10/21/89)

In article <DML.89Oct17135223@bloch.esl.com> dml@esl.com (Denis Lynch) writes:
>First, the "smart compiler" problem. Since reusable code is likely to go in a
>library, the compiler can't possibly tell whether an operator is overloaded.
>Unless you expect to have access to the source code for all your libraries,
>which the library vendors aren't likely to be excited about.

Actually, I do expect that I will commonly have access to all sources (but
then I work for a medium sized company that can afford it).  You are right in
that there will be exceptions, and the compiler/linker have to handle it.  I
don't see it being a very difficult problem (as long as we are taling about a
"true" C++ compiler/linker, instead of being limited to what C can do).

>Second, the "anticipation" problem. Here, you must be arguing that you want
>*all* member functions to be virtual, i.e. overridable. If that's what you
>want, you don't need a smart compiler or a language change, just declare all
>your functions virtual.

And take a performance hit (that's what started this thread of discussion).

>The current language gives you the option of writing member functions that you
>*know* will not be overriden, and others that may be. This is a reasonable
>extension of the public/private/protected distinctions: you can have code
>that is guaranteed to call the function you wrote, never a client's "better
>idea."
>
>Or did I miss something?

The idea is that, for any public routine, the person *using* the code should
make the decision, not the original author.  C++ takes just the opposite stand.
I am sensitive to the reasons why, but I still feel it is a wart in C++.
 
 
Brian McElhinney		  "Knowledge is soon changed, then lost in the
mce@tc.fluke.com		   mist, an echo half-heard"      --Gene Wolfe