35. 考虑virtual函数以外的其他选择
- 令客户通过public non-virtual成员函数间接调用private virtual函数。
- 继承类可以重新定义继承而来的private virtual函数
- 该条款主要介绍替代virtual函数的方法,主要有:
1)用private virtual函数替代:即原本用virtual函数地方改成用非virtual函数,并在这里面调用private virtual函数。
2)用函数指针替换virtual函数,(同一函数指针是要求返回值类型+参数列表个数和类型相同),继承类对该函数指针可以有不同的定义即可(即定义拥有相同返回值类型和参数列表个数和类型的函数),从而发挥virtual函数作用
3)用tr1::function替代virtual函数。这个和函数指针区别在于tr1::function支持返回值+参数列表的隐式转换。
总结:该条款介绍了替代virtual函数方式,但我没想明白我们是出于什么考虑不选择用virtual函数,而选择用这些替代方法。
从条款37内容看,似乎是出于这样的考虑:缺省参数值的virtual函数是静态绑定的,如果想不重复定义继承而来的缺省参数值的virtual函数,并同时提供缺省参数值给基类和继承类的用户,即保证在基类和继承类中缺省参数值的virtual函数相同。如果基类的缺省参数值改变,那么这些继承类也必须改变,否则就编变成了重复定义继承而来的缺省参数值的virtual值,这时候就需要替代virtual函数。即用这些替代方式替代缺省参数值的virtual函数。
36. 绝不重新定义继承而来的non-virtual函数
-
non-virtual函数是静态绑定的,virtual函数是动态绑定的。
class B {public:void mf(); }class D: public B {public:void mf(); }D x; B *pB = &x; pB->mf(); // 1D *pD = &x; pD->mf(); // 2
若mf是non-virtual函数,那么1调用的是B的mf,2调用的D的mf,因为这是静态绑定,在编译期间就固定了。(也就是之前说的遮蔽问题)
若mf是virtual函数,那么1和2调用的是D的mf,因为pB和pD真正指的是类型为D的对象。
总结:绝对不要重新定义继承而来的non-virtual函数,因为这样会为基类反映“不变性凌驾特性”的性质。导致指向继承类的基类指针调用的成员函数是基类的而不是继承类的。
37. 绝不重新定义继承而来的缺省参数值
- virtual函数是动态绑定的,而virtual缺省参数值却是静态绑定的。
动态绑定是调用一个virtual函数,究竟调用哪一份函数实现代码,取决于发出调用的那个对象的动态类型。即等号右边的类型。
静态绑定是调用一个virtual缺省参数值(即有默认参数值),是取决于等号左边的类型。 - c++如此运作的原因是——运行期效率。
总结:绝不重新定义继承而来的缺省参数值(即有默认参数值)的virtual函数,因为其是静态绑定的。
38. 通过复合塑模出has-a或“根据某物实现出”
- 复合是类型之间的一种关系,即当某种类型的对象内含该种类型的对象,就是这种关系。
- 复合这个术语有这些同义词:layering(分层)、containment(内含)、aggregation(聚合)和embedding(内嵌)
- 复合意味着有一个或根据某物实现出,当需要在软件中处理两个不同的领域。当复合发生于应用域内的对象之间,表现出has-a的关系;当其发生在实现域内则表现根据某物实现出。
- 当不知道是选择is-a还是has-a时候,想想is-a的定义:如果D是一种B,对B为真的每一件事对D也都应该为真。
总结:复合的意义和public继承完全不同,当不知道选择is-a还是复合的has-a或is-implemented-in-terms-of时,想想is-a的定义以及复合的定义,如果是该对象要做另外个领域的事情,那么应该选择用复合。
例如自己定义一个set模板,就应该复合list而不是public继承list,因为list不支持只存储不重复数据。
39. 明智而审慎地使用private继承
- private继承并不意味这is-a关系,意味着implement-in-terms-of(根据某物实现出)。即如果要D以private形式继承B,用意就是采用B内已经备好的某些特性。尽可能用复合,必要时用private继承。【必要时指的是protected成员和/或virtual函数牵扯进来时候】
- private继承意味着只继承实现部分,没有继承接口部分。即其在软件“设计”层面没有意义,其意义在软件实现层面。
- private继承有两条规则:
1)classes之间的继承关系是private时,编译器不会自动将一个继承类对象转为基类对象。
2)由基类private继承而来的所有成员,在继承类中都会变成private属性。 - private继承主要用于当一个意欲成为继承类想访问一个意欲成为基类的protected部分,或为了重新定义一个或多个virtual函数。
- private继承可以造成empty base最优化。
总结:虽说private继承也能体现复合的一种,但是能用复合就不用private继承,除非遇到要重新定义继承而来的virtual函数或继承类要访问基类的基类protected成员。