面向对象编程(OOP)几乎已经风靡两个时代了,所以关于继承、派生、virtual 函数等等,可能你已经有了一些经验。纵使你过去只以 C 编写程序,如今肯定也无法逃脱 OOP 的笼罩。
尽管如此,C++ 的 OOP 有可能和你原本习惯的 OOP 稍有不同:“继承” 可以是单一继承或多重继承,每一个继承连接(link)可以是 public,protected 或 private,也可以是 virtual 或 non-virtual 。然后是成员函数的各个选项:virtual ? non-virtual ? pure virtual ? 以及成员函数和其他语言特性的交互影响:缺省参数值与 virtual 函数有什么交互影响?继承如何影响 C++ 的名称查找规则?设计选项有哪些?如果 class 的行为需要修改,virtual 函数是最佳选择吗?
本章对这些题目全面宣战。对此我也解释 C++ 各种不同特性的真正意义,也就是当你使用某个特定构件你真正想要表达的意思。例如 “public继承” 意味着 “is-a”,如果你尝试让它带着其他意义,你会惹祸上身。同样道理,virtual 函数意味着 “接口必须被继承”,non-virtual 函数意味 “接口和实现都必须被继承” 。如果不能区分这些意义,会造成 C++ 程序员大量的苦恼。
如果你了解 C++ 各种特性的意义,你会发现,你对 OOP 的看法改变了。它不再是一项用来划分语言特性的仪典,而是可以让你通过它说出你对软件系统的想法。一旦你知道该通过它说些什么,转移至 C++ 世界也就不再是可怕的高要求了。
derived classes 内的名称会遮掩 base classes 内的名称。在 public 继承下从来没有人希望如此。
为了让被遮掩的名称再见天日,可使用 using 声明式或转交函数(forwarding functions)。
接口继承和实现继承不同。在 public 继承之下,derived classes 总是继承 base class 的接口。
pure virtual 函数只具体制定接口继承。
简朴的(非纯)impure virtual 函数具体指定接口继承及缺省实现继承。
non-virtual 函数具体指定接口继承以及强制性实现继承。
virtual 函数的替代方案包括 NVI 手法及 Stratagy 设计模式的多种形式。NVI 手法自身是一个特殊形式的 Template Method 设计模式。
将机能从成员函数移到 class 外部函数,带来的一个缺点是,非成员函数无法访问 class 的 non-public 成员。
tr1::function 对象的行为就像是一般函数指针。这样的对象可接纳 “与给定之目标签名式(target signature)兼容” 的所有可调用物(callable entities)。
复合(composition)的意义和 public 继承完全不同。
在应用域(application domain),复合意味 has-a(有一个)。在实现域(implementation domain),复合意味 is-implemented-in-terms-of(根据某物实现出)。
Private 继承意味着 is-implement-terms of(根据某物实现出)。它通常比复合(composition)的级别低。但是当 derived class 需要访问 protected base class 的成员,或需要重新定义继承来的 virtual 函数时,这么设计是合理的。
和复合(composition)不同,private 继承可以造成 empty base 最优化。这对致力于 “对象尺寸最小化” 的程序库开发者而言,可能很重要。
多重继承比单一继承复杂。它可能导致新的歧义性,以及对 virtual 继承的需要。
virtual 继承会增加大小、速度、初始化(及赋值)复杂度等等成本。如果 virtual base classes 不带任何数据,将是最具实用价值的情况。
多重继承的确有正当用途。其中一个情节设计 “public 继承某个 Interface class” 和 “private 继承某个协助实现的 class” 的两相组合。