理解C++面向对象

“继承和面向对象设计” 的简介中,我曾强调,理解不同的面向对象构件在C++中的含义十分重要。这和仅仅知道C++语言的规则有很大的不同。例如,C++规则说,如果类D从类B公有继承,从D的指针到B的指针就有一个标准转换;B的公有成员函数将被继承为D的公有成员函数,等等。这些规则都是正确的,但在将设计思想转化为C++的过程中,它们起不到任何作用。相反,你需要知道,公有继承意味着 “是一个”,如果D从B公有继承,类型D的每一个对象也 “是一个” 类型B的对象。因而,如果想在设计中表示 “是一个”,就自然会想到使用公有继承。

“说出你想说的” 只是成功的一半。事情的另一面是 “理解你所说的”,这一点同样重要。例如,将成员函数声明为非虚函数会给子类带来限制,如果没有认识到这一点就随便这样做将是不负责任的行为 —- 除非你完全是有意这么做。声明一个非虚成员函数,你实际上是在说这个函数表示了一种特殊性上的不变性;如果不明白这一点,将会给程序带来灾难。

公有继承和 “是一个” 的等价性,以及非虚成员函数和 “特殊性上的不变性” 的等价性,是C++构件如何和设计思想相对应的例子。下面的列表总结了这些对应关系中最重要的几个。

· 共同的基类意味着共同的特性。如果类D1和类D2都把类B声明为基类,D1和D2将从B继承共同的数据成员和/或共同的成员函数。明智地使用多继承
· 公有继承意味着 “是一个”。如果类D公有继承于类B,类型D的每一个对象也是一个类型B的对象,但反过来不成立。使公有继承体现 “是一个” 的含义
· 私有继承意味着 “用…来实现”。如果类D私有继承于类B,类型D的对象只不过是用类型B的对象来实现而已;类型B和类型D的对象之间不存在概念上的关系。见条款42。
· 分层意味着 “有一个” 或 “用…来实现”。如果类A包含一个类型B的数据成员,类型A的对象要么具有一个类型为B的部件,要么在实现中使用了类型B的对象。通过分层来体现 “有一个” 或 “用…来实现”

下面的对应关系只适用于公有继承的情况:

· 纯虚函数意味着仅仅继承函数的接口。如果类C声明了一个纯虚函数mf,C的子类必须继承mf的接口,C的具体子类必须为之提供它们自己的实现。区分接口继承和实现继承
· 简单虚函数意味着继承函数的接口加上一个缺省实现。如果类C声明了一个简单(非纯)虚函数mf,C的子类必须继承mf的接口;如果需要的话,还可以继承一个缺省实现。
· 非虚函数意味着继承函数的接口加上一个强制实现。如果类C声明了一个非虚函数mf,C的子类必须同时继承mf的接口和实现。实际上,mf定义了C的 “特殊性上的不变性”。