bublic继承意味着“is-a”关系。即一个D类对象(Derived)同时也是一个B类对象(Base)。D is-a B。反之则不成立。B相比D更一般化,而D相对B更特殊化。
考虑如下:
class Person {…};
class student: public Person{…};
承上,C+中任何函数期望获得类型Person(指针或引用)的实参,也愿意接受Student对象(指或引)、void eat(const person& p); //通用行为
void study(const student& s); //学生专属行为
person p;
student s;
eat(p); //ok
eat(s); //ok 学生也会eat
study(s); //ok
study(p); //错误,p不是学生
上述论点只能public继承才成立。private继承意义完全不同。 public继承和is-a之间的等价关系看起来简单,实则不然。如下鸟和企鹅的例子。class Bird
{
public:
virtuan void fly(); //鸟可以飞
};
clas Penguin:public Bird
{…..}; //企鹅是一种鸟
我们成了语言不严谨的牺牲品,因为前提是并不是所有的鸟都会飞。 应用以下方法模拟出较佳的真实性。class Bird
{
…. //没有声明fly函数
};
class flyBird : public Bird
{
public:
virtual void fly();
….
};
class Penguin: public Bird
{
… //没有声明fly函数.
};
虽然这样,但我们仍然未能处理好这事情,因为有些系统并不关心会飞与否的问题,这样说明了一个事实,不存在一个“适用于所有软件的设计”。 另一种思想:所有鸟会飞,企鹅是鸟,但不会飞。重新定义fly,令其产生一个运行时错误 。void error(const std::string& msg); //定义于其他某处
class Penguin:public Bird
{
public:
virtual void fly(){error(“attempt to fly”);}
…
};
这里并不是说其不会飞,而是其会飞是一种错误。这是在运行期间检测出的错误。 考虑另一种思路,即“不会飞”,不可以为其定义fly函数class Bird
{
…. //没有声明fly函数
};
class Penguin: public Bird
{
… //没有声明fly函数.
};
现在,当试图飞时: Penguin p;
p.fly(); //编译器会报错误
这和上一种思想的不同之处是,第一种方法在编译期间对p.fly不会发生错误,TK18说过,好的接口可以不让无效代码通过编译 ,因此对比我们应该采取第二种方式 ,即在编译期间就拒绝无效代码 再考虑以下简单的公有继承,正方形public继承至矩形。我们都知道正方形是矩形,反之不然。 class Rectangle{
public:
virtual void setH{int newH};
virtual void setW{int newW};
virtual int height() const; //返回当前值
virtual int width() const;
…
};
void makeBig(Rectangle& r) //增加r的面积
{
int oldH=r.height();
r.setW(r.width()+10); //r的宽度加上0
assert(oldH==r.height()); //判断r高是否改变
}
显然,上述asser结果永远为真。因为r的高未曾改变过 现在考虑下述代码,正方公有继承矩形。则正方形被视为矩形class Square:public Rectangle{…};
Square s;
assert(s.width()==s.height()); //真
makeBig(s);//public,s is-a矩形 可以增加其面积
assert(s.width()==s.height());
很明显,按理第二个assert应该为真,但其却不同了(注意传递的是引用)public主张的事情: 施行于基类对象的事情也能施行于派生类上,但本例矩形的方法明显不适用于正方。就如我们学过的:代码通过编译并不表示就可以正确运作。 is-a并不是唯一存在的类之间的关系。另外的有has-a (有一个)和is-implemented-in-terms-of (根据某物实现出),将这些作为is-a都是错误的。所以应该确实了解这些类之间的相互关系并塑造他们。
需要记住的:
1、public继承意味着is-a。适用于base的也适用于derived.因为每个派生类对象也是一个基类对象。