6 继承和面向对象设计
条款32:确定你的public继承塑模出is-a关系。
如果你令classD以public形式继承class B,你便是告诉C++编译器说,每一个类型为D的对象同时也是一个类型为B的对象。那么使用于base classes身上的每一件事情一定也适用于derived classes身上,因为每一个derived class对象也都是一个base class对象。
条款33:避免遮掩继承而来的名称。
这个读取数据的语句指涉的是local变量x,而不是global变量x,因为内层作用域的名称会掩盖外围作用域的名称。
现在导入继承,派生类作用域被嵌套在base class作用域内,像:
假设derived class内的mf4的实现码部分像这样:
当编译器看到这里使用名称mf2,必须估算它指涉什么东西。编译器的做法是查找各作用域。
base class内所有名为mf1和mf3的函数都被derived内的mf1和mf3函数遮掩掉了。
或者使用Inline转交函数
请记住:
derivedclasses内的名称会遮掩base classes内的名称。在Public继承下从来没有人希望如此。
为了让被掩盖的名称再见天日,可使用using声明式或转交函数。
条款34:区分接口继承和实现继承。
身为class的设计者,有时候你会希望derived classes只继承成员函数的接口(也就是声明);有时候你又会希望派生类同时继承函数的接口和实现,但又希望能够覆写它们所继承的实现;又有时候你希望派生类同时继承函数的接口和实现,并且不允许覆写任何东西。
Shape是个抽象类;他的虚函数draw使它成为一个抽象类。所有客户不能够创建它的实体。
声明一个虚函数的母的就是为了让derived classes只继承函数接口;
声明质朴(非纯)虚函数的目的,是让derived classes继承该函数的接口的缺省实现。
我们可以利用“pure virtual函数必须在derived class中重新声明,但它们可以拥有自己的实现”
声明non-virtual函数的目的是为了令derived classes继承函数的接口及一份强制性实现。
条款35:考虑virtual函数以外的其他选择。
藉由Non-Virtual Interface手法实现Template Method模式
藉由Function Pointers实现Strategy模式
藉由tr1::function完成Strategy模式
古典的Strategy模式:
条款36:绝不重新定义继承而来的non-virtual函数。
造成此一两面行为的原因是,non-virtual函数如B::virtual函数如B::mf和D::mf都是静态绑定。这意思是,pb被声明为一个b的指针,通过pb调用的non-virtual函数永远是B所定义的版本。
条款37:绝不重新定义继承而来的缺省参数值。
继承一个带有缺省参数值的virtual函数,virtual函数系动态绑定,而缺省参数值确实静态绑定。
所谓静态类型,就是它在程序中被声明时所采用的类型,考虑以下的class继承体系:
对象的所谓动态类型则是指“目前所指对象的类型”。也就是说,动态类型可以表现出一个对象将会有什么行为。
因为virtual函数系动态绑定,而缺省参数值确实静态绑定,所以可能有:
虽然Rectangle::draw()函数的缺省参数值应该是GREEN
条款38:通过复合塑模出has-a或“根据某物实现出”。
复合是类型之间的一种关系,当某种类型的对象内含它种类型的对象,便是这种关系。
请记住:
复合的意义和public继承完全不同;
在应用域,复合意味had-a(有一个)。在实现域,复合意味(根据某物实现出)。
条款39:明智而审慎的使用private继承。
如果classes之间的继承关系是Private,编译器不会自动将一个derived class对象转换为一个base class对对象。第二是,由private base class继承而来的所有成员,在derived class中都会变成private属性。
private继承纯粹只是一种实现技术。如果D以private继承了B,你的用意是为了采用B
中已经备妥的某些特性。
private继承意味着根据某物实现出。所以尽可能使用复合,必要时才使用private继承。
private继承主要用于“当一个意欲成为derived class者想访问一个意欲成为base class
者的protected成分,或为了重新定义一或多个virtual函数”。
Empty是个空类,sizeof(HoldAnInt)==sizeof(int)。这是所谓的EBO(空白基类最优化)。
和复合不用,private继承可以造成empty base最优化。这对致力于“对象尺寸最小化”
的程序库开发者而言,可能很重要。
条款40:明智而审慎的使用多重继承。
多重继承可能从一个以上的base classes继承相同名称。那会导致较多的歧义,例如:
两个checkouts有相同的匹配程度,没有所谓最佳匹配。为了解决:
为了防止数据重复,应该采用虚继承:
请记住:
多重继承比单一继承复杂。它可能导致新的歧义性,以及对virtual继承的需要。
virtual继承会增加大小,速度,初始化复杂度等等成本。如果virtualbase clases不带
任何数据,将是最具使用价值的情况。
多重继承的正当用途。其中一个情节涉及“public继承某个Interface class”和“private
继承某个协助实现的class”的两相组合。