FAQ 8.14
如果"苹果袋"以kind-of方式继承至"水果袋",那么"水果袋"中就允许放入任何种类以kind-of方式从水果继承来的具体水果么?
原文描述 :Is bag-of-apple a kind-of bag-of-fruit, assuming bag-of-fruit allows the insertion of any kind-of fruit?
下表中, Fruit是一个基类, Apple和Banana是Kinds-of方式继承至Fruit类的具体的类.下面是UML框图和代码:
下面的BagOfFruit类允许插入和移除任何kind-of Fruit.
#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_]; }
下面证明了一个多态函数将任何种类的Fruit放到任何种类的BagOfFruit中去.注意.既然BagOfFruit类能担保接受所有种类的水果那么bag.insert()是正确的
void insertFruitIntoBag(BagOfFruit& bag, Fruit& fruit)
{
bag.insert(fruit);
}
下面的BagOfApple类是 kind-of BagOfFruit.但是BagOfApple并不能替代BagOfFruit.这个类还有其他几个问题,它用了一个引用,而且隐藏了BagOfFruit::remove() 和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(); }
因为类BagOfApple 是继承至BagOfFruit,BagOfApple对象可以传递给insertFruitIntoBag().不幸的是,这允许毫无意义的包和水果的组合传递给insertFruitIntoBag().例如,banana可以被放入到BagofApples.
int main()
{
BagOfApple bagOfApple;
Banana banana;
insertFruitIntoBag(bagOfApple, banana);
cout << "Removing an Apple from bagOfApple: ";
Apple& a2 = bagOfApple.remove();
a2.printClassName();
}
"Removing an Apple from bagOfApple: Banana"
在remove()中的指针(引用)很另人烦,但是真正的问题在于不恰当的继承.继承必须用一个严格的标准和可替代的方式来评价,因为知觉总是与事实不符.