FAQ 8.14 Is bag-of-apple a kind-of bag-of-fruit, assuming bag-of-fruit allows the insertion of any kind-of fruit?

graphics/new_icon.gif

NO!

This is a specific example of the general guideline presented in the previous FAQ.

In the following, Fruit is an ABC, and Apple and Banana are concrete kinds-of Fruit. The UML diagram and the code are both shown.

graphics/08fig02.gif

 #include <iostream> using namespace std; class Fruit { public:   virtual void printClassName() const throw() = 0;   virtual ~Fruit() throw(); }; Fruit::~Fruit() throw() { } class Apple : public Fruit { public:   virtual void printClassName() const throw(); }; void Apple::printClassName() const throw() { cout << "Apple\n"; } class Banana : public Fruit { public:   virtual void printClassName() const throw(); }; void Banana::printClassName() const throw() { cout << "Banana\n"; } 

The following BagOfFruit class allows insertion and removal of objects of any kind-of Fruit.

 class Full  { }; class Empty { }; class BagOfFruit { public:   BagOfFruit() throw();   unsigned size() const throw();   void insert(Fruit& f) throw(Full);   Fruit& remove() throw(Empty); protected:   enum     { maxSize_ = 20 };   unsigned   size_;   Fruit*     data_[maxSize_]; }; BagOfFruit::BagOfFruit() throw() : size_(0) { } unsigned BagOfFruit::size() const throw() { return size_; } void BagOfFruit::insert(Fruit& f) throw(Full) {   if (size_ == maxSize_) throw Full();   data_[size_++] = &f; } Fruit& BagOfFruit::remove() throw(Empty) {   if (size_ == 0) throw Empty();   return *data_[--size_]; } 

The following demonstrates a polymorphic function that inserts any kind of Fruit into any kind of BagOfFruit. Note that the parameter for bag.insert() is correct, since class BagOfFruit guarantees that this member function can accept any kind of Fruit.

 void insertFruitIntoBag(BagOfFruit& bag, Fruit& fruit) {   bag.insert(fruit); } 

The following BagOfApple class claims (by inheritance) to be a kind-of BagOfFruit. However, BagOfApple is not substitutable for BagOfFruit. There are several other things wrong with this class as well; it uses a reference cast, and it hides BagOfFruit::remove() and BagOfFruit::insert(Fruit&).

 class BagOfApple : public BagOfFruit { public:   BagOfApple() throw();   void insert(Apple& a) throw(Full);   Apple& remove() throw(Empty); }; BagOfApple::BagOfApple() throw() : BagOfFruit() { } void BagOfApple::insert(Apple& a) throw(Full) { BagOfFruit::insert(a); } Apple& BagOfApple::remove() throw(Empty) { return (Apple&) BagOfFruit::remove(); } 

Because class BagOfApple inherits from class BagOfFruit, BagOfApple objects can be passed to insertFruitIntoBag(). Unfortunately, this permits nonsensical combinations of bags and fruits to be passed to insertFruitIntoBag(). For example, a banana can be inserted into a BagOfApples.

 int main() {   BagOfApple bagOfApple;   Banana banana;   insertFruitIntoBag(bagOfApple, banana);   cout << "Removing an Apple from bagOfApple: ";   Apple& a2 = bagOfApple.remove();   a2.printClassName(); } 

The output of this program follows.

 Removing an Apple from bagOfApple: Banana 

The pointer (reference) cast in the remove() member function can be blamed, but the real culprit is improper inheritance. Inheritance must be evaluated using substitutability, a rigorous criterion, because intuition is often wrong.



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