Item 39 私有继承

来源:互联网 发布:美国矩阵风投公司资料 编辑:程序博客网 时间:2024/05/19 13:07

C++编译器把公有继承的类当作基类的特例,私有继承则不同:

 

 

可以看到,编译器并不把派生类对象转化为基类对象。
而且,所有基类的成员函数在派生类里都成为私有的。

公有继承中,基类和派生类有“类型”上的一致性;私有继承中,派生类只是对基类的某些成员函数感兴趣,借助它们实现功能。
所以,私有继承只继承了所有的“实现”细节,并不继承基类的接口。所以,私有继承和软件设计没关系,只和软件实现有关系。

Item 38引入的复合模式也是针对实现的。那么在实现时,如何在二者之间选择?答案:
1> 尽量用复合模式。
2> 如果涉及到protected member或者虚函数,考虑私有继承。
3> 一种极端情况。

看下例的widget。
如果一个软件的执行是分阶段的,那么不同的阶段肯定会有不同的执行模式。我们要跟踪每个成员函数的执行,然后定时汇报情况,同时输出widget的状态。所以我们需要Timer类:

 

 

不过,要想自定义onTick,必须使用继承!而且公有继承不行,因为Widget明显不是Timer,二者不能在类型上有继承关系。也不能让Widget客户使用onTick这个接口。所以:

 

 

如果不使用私有继承,使用“复合 + 公有继承”的话:

 

 

虽然可行,但是麻烦。

使用“复合 + 公有派生”这种方案是这么考虑的:
1> 你想让Widget有被派生的能力,但是又不想让其onTick被重定义。那么就不能让Widget直接从Timer派生,私有派生也不行。Item 35说了,派生类即使不能访问,也能重定义派生来的虚函数。所以Widget自建了一个私有的WidgetTimer类,只供自己使用,不让派生类染指。
2> 可以减小Widget对Timer的编译依赖。Widget如果从Timer派生,必须要#include "Timer.h",以便在编译时找到其定义。这就是依赖了!使用WidgetTimer,可以只用一个指针,然后把WidgetTimer移到外面,则去掉了依赖。

使用私有继承的极端情况:你要处理一个不占内存的类(该类没有non-static数据成员,没有虚函数,没有虚基类)。
但出于技术原因,C++不允许出现空类!

 

 

此时,sizeof(HoldsAnInt) > sizeof(int)。而且大多数编译器里,sizeof(Empty) = 1。C++会在空类里悄悄插个char。而且编译器对类做对齐处理,所以HoldsAnInt不会只有一个多余的char。
不过,空类不空,这种情况只用于那些freestanding类。这些类不和别的类构成继承关系。有继承关系就不一样了:

 

 

此时,sizeof(HoldsAnInt) == sizeof(int)。这有个编译器专门术语:EBO——empty base optimization。EBO只存在于单继承。

原创粉丝点击