public继承应当是"is-a"的关系。当Derived public继承自Base时, 相当于你告诉编译器和所有看到你代码的人:Base是Derived的抽象,Derived就是一个Base,任何时候Derived都可以代替Base使用。Base class拥有的方法,在Derived对象调用起来仍然合理。
书中给出了两个例子让读者加深印象,第一个例子代码如下:
class Bird{
public:
vitural void fly();
};
class Penguin: public Bird{
// fly??
};
Penguin
继承自Bird
,但企鹅不会飞,
这时你可能会困惑Penguin
到底是否应该有fly()
方法。但其实这个问题来源于自然语言的二义性: 严格地考虑,鸟会飞并不是所有鸟都会飞。我们对会飞的鸟单独建模便是:
class Bird{...};
class FlyingBird: public Bird{
public:
virtual void fly();
};
class Penguin: public Bird{...};
这样当你调用penguin.fly()时便会编译错。当然另一种办法是Penguin继承自拥有fly()方法的Bird, 但Penguin::fly()中抛出异常。这两种方式在概念是有区别的:前者是说企鹅不能飞;后者是说企鹅可以飞,但飞了会出错。哪种实现方式好呢?接口应当设计得不容易被误用,最好将错误从运行时提前到编译时。所以前者更好!
第二个例子:
根据学校中的知识,正方形是一种特殊的矩形。但是转换到OOP中是不正确的,代码如下,正方形对象s执行完makeBigger后,它的长和宽不相等了,然而makeBigger却破坏了正方形的属性, 所以正方形并不是一个矩形(因为矩形需要有这样一个性质:增加宽度时高度不会变。
class Rect{...};
void makeBigger(Rect& r){
int oldHeight = r.height();
r.setWidth(r.width()+10);
assert(r.height() == oldHeight);
}
class Square: public Rect{...};
Square s;
assert(s.width() == s.height());
makeBigger(s);
assert(s.width() == s.height());