FAQ 8.05 Should an overridden virtual function throw an exception?

Only if the base class says it might.

It's appropriate for an override to throw an exception if and only if the specification of the member function in the base class says it might (there is a special case for legacy code that considers the absence of an exception specification in the base class to allow the derived class to throw whatever it wants, but this is for historical reasons only). Without such a specification in the base class, throwing an exception in an override is similar to false advertising. For example, consider a used-car salesman selling a kind-of car that blows up (that is, throws an exception) when you turn on the ignition switch. Ralph Nader would correctly say that such a vehicle isn't substitutable for a car. Code should do what the specification says it will do, or other code that relies on those promises may break.

Also, consider the Ostrich / Bird dilemma. Suppose Bird::fly() promises never to throw an exception, as follows.

 #include <iostream> using namespace std; class Bird { public:   Bird() throw();   virtual ~Bird() throw();   int altitude() const throw();   virtual void fly() throw();     // PROMISE: altitude() will return a number > 0; never     // throws an exception protected:   int altitude_; }; Bird::Bird() throw() : altitude_(0) { } Bird::~Bird() throw() { } int Bird::altitude() const throw() { return altitude_; } void Bird::fly() throw() { altitude_ = 100; } 

Based on this promise, it is legitimate and appropriate to assume that the fly() member function will not throw an exception. For example, the following sample code is decorated with a throw(), meaning that this function promises not to throw an exception.

 void sample(Bird& bird) throw()                      <-- 1 {   bird.fly(); } 

(1) Legitimate reliance on what Bird::fly() says

But suppose Ostrich::fly() is defined to throw an exception, as follows.

 class CannotFly { }; class Ostrich : public Bird { public:   virtual void fly() throw(CannotFly);     // PROMISE: Throws an exception despite what Bird says }; void Ostrich::fly() throw(CannotFly) { throw CannotFly(); } 

Now suppose someone legitimately passes an Ostrich into the sample() code:

 int main() {   Ostrich bird;   sample(bird);                                      <-- 1 } 

(1) Legitimate conversion from Ostrich to Bird

Unfortunately the program will crash in the sample() function, since the fly() member function ends up throwing an exception. One cannot blame main() for passing an Ostrich into the sample() function; after all, Ostrich inherited from Bird and therefore Ostrich is supposed to be substitutable for Bird. One cannot blame sample() for believing the promise made by Bird::fly(); indeed programmers are supposed to rely on the specification rather than the implementation. So the blame rests with the author of class Ostrich, who claimed that Ostrich was substitutable for Bird even though it didn't behave like a Bird.

The lesson is that improper inheritance cannot be fixed by throwing an exception if the base class prohibits the throwing of an exception. This is because the root of improper inheritance is behavior that violates a contract, and throwing an exception is part of a function's behavior. Specifically, the behavior of an overridden virtual function that throws an exception conflicts with a base class contract that prohibits the throwing of an exception.

For an exception to this guideline, see FAQ 26.12.



C++ FAQs
C Programming FAQs: Frequently Asked Questions
ISBN: 0201845199
EAN: 2147483647
Year: 2005
Pages: 566
Authors: Steve Summit

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net