shankar@hpclscu.HP.COM (Shankar Unni) (09/13/90)
PURE VIRTUAL DESTRUCTORS:
One of our users came up with an interesting construct:
class Base {
virtual ~Base() = 0; // pure virtual destructor
};
class Derived { /*...*/ };
// ...
Derived *p = new Derived();
delete p;
// ...
The problem is, cfront does not like pure virtual destructors. In fact, my
reading of the 2.1 ARM (section 12.4) seems to imply that since
"destructors cannot be inherited", such code should not make sense, because
making a function pure virtual means that you are forcing it to be
redefined in each of its derived classes, and since a destructor cannot be
redefined in a base class, this should be meaningless for destructors.
Indeed, it doesn't work: cfront emits a call to "Base::~Base()" when
deleting "p", which leads to a link-time unresolved).
The key point seems to be: the language insists that every class have a
destructor. There is *no way* for a user to say: "I don't want a destructor
function for this class. Just return the object to memory when "delete" is
called on this object". Every destruction of an object unconditionally
generates a call to the destructor of each of its base classes, whether or
not they are "pure virtual".
How good (or bad) an idea is it to support such an extension to the meaning
of "= 0" on a virtual member function declaration? I mean, if the user
specifies a destructor as "pure virtual", then:
(a) don't check for redefinitions in inherited classes (cfront doesn't
do this now, anyway), *and*,
(b) don't emit any calls to that destructor, when an object of a class
inherited from it is destroyed.
Seems easy enough to do - is there any major semantic problem with this?
-----
Shankar Unni E-Mail:
Hewlett-Packard California Language Lab. Internet: shankar@hpda.hp.com
Phone : (408) 447-5797 UUCP: ...!hplabs!hpda!shankar
shopiro@alice.UUCP (Jonathan Shopiro) (09/14/90)
In article <77210003@hpclscu.HP.COM>, shankar@hpclscu.HP.COM (Shankar Unni) writes: > PURE VIRTUAL DESTRUCTORS: > > One of our users came up with an interesting construct: > > class Base { > virtual ~Base() = 0; // pure virtual destructor virtual void f() = 0; // example pure virtual function > }; > > class Derived { /*...*/ }; If you want to do this you must define the Base destructor, e.g. Base::~Base() {} It is a little-known (and perhaps unfortunate) fact that pure virtual functions can be defined. They can only be called through the explicitly qualified name, e.g., Base* bp = new Derived; bp->Base::f(); would call Base::f() if it has been defined, and cause a link error otherwise. The destructor is a slightly special case (:-)), since the compiler automatically generates the equivalent of an explicitly qualified call to it in the destructor of the derived class. An alternative way to define the language would be to disallow defining pure virtual functions and (therefore) disallow pure virtual destructors. This would give the programmer somewhat less flexibility but would not leave the compiler wondering whether a pure virtual function was going to be defined or not. -- Jonathan E. Shopiro AT&T Bell Laboratories, Warren, NJ 07059-0908 shopiro@research.att.com (201) 580-4229
steve@taumet.com (Stephen Clamage) (09/14/90)
shankar@hpclscu.HP.COM (Shankar Unni) writes: >PURE VIRTUAL DESTRUCTORS: >One of our users came up with an interesting construct: > class Base { > virtual ~Base() = 0; // pure virtual destructor > }; I really don't see what problem this user is trying to solve. No class is required to have a destructor, so if you don't want a destructor called for class Base, don't define one. Virtual destructors are a fine idea. To enforce virtual destructors in a hierarchy, put an empty virtual destructor in the most base class: class Base { ... virtual ~Base(){} ... }; If you wish to create an abstract class, pure virtual functions are a big help. Define the abstractions to be realized in derived classes as pure virtual in the base class. The destructor is not such a candidate. If a base class has a destructor, it must be called when a derived class is destroyed; if the destructor is pure virtual it cannot be called. Messing up the semantics of destructors to accomodate this idea would have to be compensated by some real advantage. I can't see that there is any advantage in pure virtual destructors. -- Steve Clamage, TauMetric Corp, steve@taumet.com
shankar@hpclscu.HP.COM (Shankar Unni) (09/18/90)
> It is a little-known (and perhaps unfortunate) fact that pure virtual > functions can be defined. They can only be called through the > explicitly qualified name, e.g., > > Base* bp = new Derived; > bp->Base::f(); > > would call Base::f() if it has been defined, and cause a link error > otherwise. The destructor is a slightly special case (:-)), since the > compiler automatically generates the equivalent of an explicitly > qualified call to it in the destructor of the derived class. > > An alternative way to define the language would be to disallow > defining pure virtual functions and (therefore) disallow pure virtual > destructors. This would give the programmer somewhat less flexibility > but would not leave the compiler wondering whether a pure virtual > function was going to be defined or not. Yes, I remember filing a bug report about this, not realizing it was a feature :-(. However, the point (and it's not one that I'm going to beat around a lot - this was mostly an information query), I think, was that he wanted to have a destructor defined in a way such that *nothing* was called on behalf of the base class component of the object. Stephen Clamage writes: > I really don't see what problem this user is trying to solve. No class > is required to have a destructor, so if you don't want a destructor > called for class Base, don't define one. But that's precisely the problem: if you don't define a destructor for a class, *cfront will define one for you*; and call it when the object is destroyed. This destructor happens to be a null inline, so when destroying a standalone object of that class, nothing happens; however, when destroying an object of a derived class, when cfront gets around to the base class component, it puts out an out-of-line call to the null destructor (because it's virtual). Anyway, never mind; I'm convinced... ---- Shankar Unni.
mike@taumet.com (Michael S. Ball) (09/19/90)
In article <77210004@hpclscu.HP.COM> shankar@hpclscu.HP.COM (Shankar Unni) writes: >But that's precisely the problem: if you don't define a destructor for a >class, *cfront will define one for you*; and call it when the object is >destroyed. This destructor happens to be a null inline, so when destroying >a standalone object of that class, nothing happens; however, when >destroying an object of a derived class, when cfront gets around to the >base class component, it puts out an out-of-line call to the null >destructor (because it's virtual). Base class destructors called from within a derived class destructor are not called using the virtual mechanism. It's the equivalent of calling bv->B::foo() instead of b->foo(). If you saw an out-of-line call I suspect you ran into the cfront hack which prohibits inlining in expressions of a certain complexity (I believe no more than 2 inlines per expression, though I may have the number wrong.) If you saw an out-of-line virtual call, you ran into a deadly bug which will cause all virtual destructors to die the recursive death. In any case, you can't make the destructor virtual unless you define it. The compiler generated constructor won't be virtual unless it has a base class with a virtual destructor. Defining a pure virtual destructor with an inline empty body will also work, though, as mentioned above, cfront may not optimize it away. Mike Ball TauMetric Corporation