Effective C++:6、继承与面向对象设计

mac2022-06-30  125

简介:

面向对象编程(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++ 世界也就不再是可怕的高要求了。

条款32:确定你的 public 继承塑模出 is-a 关系

“public 继承” 意味 is-a 。适用于 base classes 身上的每一件事一定也适用于 derived classes 身上,因为每一个 derived class 对象也都是一个 base class 对象。

条款33:避免遮掩继承而来的名称

derived classes 内的名称会遮掩 base classes 内的名称。在 public 继承下从来没有人希望如此。

为了让被遮掩的名称再见天日,可使用 using 声明式或转交函数(forwarding functions)。

条款34:区分接口继承和实现继承

接口继承和实现继承不同。在 public 继承之下,derived classes 总是继承 base class 的接口。

pure virtual 函数只具体制定接口继承。

简朴的(非纯)impure virtual 函数具体指定接口继承及缺省实现继承。

non-virtual 函数具体指定接口继承以及强制性实现继承。

条款35:考虑 virtual 函数以外的其他选择

virtual 函数的替代方案包括 NVI 手法及 Stratagy 设计模式的多种形式。NVI 手法自身是一个特殊形式的 Template Method 设计模式。

将机能从成员函数移到 class 外部函数,带来的一个缺点是,非成员函数无法访问 class 的 non-public 成员。

tr1::function 对象的行为就像是一般函数指针。这样的对象可接纳 “与给定之目标签名式(target signature)兼容” 的所有可调用物(callable entities)。

条款36:绝不重新定义继承而来的 non-virtual 函数

绝对不要重新定义继承而来的 non-virtual 函数。

条款37:绝不重新定义继承来的缺省参数值

绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而 virtual 函数——你唯一应该覆写的东西——却是动态绑定。

条款38:通过复合塑模出 has-a 或 “根据某物实现出”

复合(composition)的意义和 public 继承完全不同。

在应用域(application domain),复合意味 has-a(有一个)。在实现域(implementation domain),复合意味 is-implemented-in-terms-of(根据某物实现出)。

条款39:明智而谨慎地使用 private 继承

Private 继承意味着 is-implement-terms of(根据某物实现出)。它通常比复合(composition)的级别低。但是当 derived class 需要访问 protected base class 的成员,或需要重新定义继承来的 virtual 函数时,这么设计是合理的。

和复合(composition)不同,private 继承可以造成 empty base 最优化。这对致力于 “对象尺寸最小化” 的程序库开发者而言,可能很重要。

条款40:明智而谨慎地使用多重继承

多重继承比单一继承复杂。它可能导致新的歧义性,以及对 virtual 继承的需要。

virtual 继承会增加大小、速度、初始化(及赋值)复杂度等等成本。如果 virtual base classes 不带任何数据,将是最具实用价值的情况。

多重继承的确有正当用途。其中一个情节设计 “public 继承某个 Interface class” 和 “private 继承某个协助实现的 class” 的两相组合。

最新回复(0)