1. public inheritance 的核心规则
在 C++ 的面向对象编程中,最重要的规则是:public inheritance 意味着 “is - a”。即当class D
从class B
公开继承时,表明每个D
类型的对象也是B
类型的对象,但反之不成立。例如:
class Person {... };
class Student : public Person {... };
这表明每个学生都是人,但并非每个人都是学生。任何期望参数类型为Person
(或pointer - to - Person
或reference - to - Person
)的函数,都可以接受Student
对象(或pointer - to - Student
或reference - to - Student
),如:
void eat(const Person& p);
void study(const Student& s);
Person p;
Student s;
eat(p);
eat(s);
study(s);
// study(p); // 错误,p不是Student
此规则仅适用于 public inheritance,private inheritance 和 protected inheritance 含义不同。
2. 违背 “is - a” 规则的案例分析
- 鸟类案例:起初可能认为企鹅是鸟,鸟能飞,于是设计如下继承体系:
class Bird {
public:
virtual void fly();
...
};
class Penguin : public Bird {
...
};
但这与事实不符,因为企鹅实际不能飞。更准确的设计应区分能飞和不能飞的鸟:
class Bird {
...
};
class FlyingBird : public Bird {
public:
virtual void fly();
...
};
class Penguin : public Bird {
...
};
当然,对于不涉及飞行处理的应用程序,最初的设计可能也适用。另外,也有人认为可让企鹅重定义fly
函数以产生运行时错误,但更好的方式是通过编译器阻止非法行为,如:
class Bird {
...
};
class Penguin : public Bird {
...
};
Penguin p;
// p.fly(); // 错误,编译器阻止该非法行为
- 几何图形案例:从数学角度,正方形是矩形,但在 C++ 中,若
Square
从Rectangle
公开继承,可能出现问题。例如:
class Rectangle {
public:
virtual void setHeight(int newHeight);
virtual void setWidth(int newWidth);
virtual int height() const;
virtual int width() const;
...
};
void makeBigger(Rectangle& r) {
int oldHeight = r.height();
r.setWidth(r.width() + 10);
assert(r.height() == oldHeight);
}
class Square : public Rectangle {... };
Square s;
assert(s.width() == s.height());
makeBigger(s);
assert(s.width() == s.height());
由于正方形宽高必须相等,而makeBigger
函数对矩形宽的改变与正方形的特性冲突,这表明在此场景下,用 public inheritance 模拟正方形和矩形的关系是错误的,尽管编译器允许,但代码行为可能不正确。
3. 总结
public inheritance 意味着 “is - a”,适用于基类的所有特性也应适用于派生类,因为每个派生类对象都是基类对象。在设计时,要确保遵循此规则,避免因违反该规则导致设计错误。同时,除了 “is - a” 关系,类之间还存在 “has - a” 和 “is - implemented - in - terms - of” 等关系,需正确理解并模拟这些关系。