private继承,在看到effective C++之前,我发现在我之前的代码里面,没有用到这个机制,但是认真回想起还是有一些影子。
大多数人认为private继承是没有用的,其实不然,在某些情况下,它也会带给你一些惊喜。
好,言归正传,之前已经数次提到过 public继承表示的是 “is a ”的关系,也就是说每一个derived对象D同时也是base对象,任何在base对象上的操作可以实施到derived对象身上。
例如:
class Person{....}; class Student::public Person{...}; Person p; Student s; void eat(Person *p); void study(Student &s); eat(s);//可以施加到Person身上的操作,对于Student对象仍然使用。 eat(p);
那么,private继承到底意味着什么,首先很直观的讲,上述的Student对象并没有转化为Person对象,其次,由
private继承而来的所有Base的成员在Derived中均是private属性的(无论其原先在Base类中的属性是public或者是
protected)。
最重要的一点,private继承意味着 “ is implemented as ...” (以某物来实现),也就意味着,Derived类只是继承
了Base类中的属性,并未继承其接口。这是实现层面上的事儿。
值得注意的是,复合 的意义也是 “ is implemented as ...” (以某物来实现),这似乎还是印证了那句 “private继承无用”。
关于此二者的权衡,很简单,在两者均可以的情况下,选择使用 复合,而不是private继承, 除非在一些很特殊的情况,接下来的几
个例子就是用来讲二者的区别。
假设:
我们需要定义一个类 Timer 来统计当前类 Widget 所处的状态,例如函数调用次数等,那么我们需要一个这样的类来实现这样的功能。
定义可以如下:
class Timer{ public: explicit Timer(int ticks); virtual ontick() const;//需要计数的类继承此函数, ... };
显然,在Widget类中需要使用Timer类的对象,采用 public 继承显然是不合适的,因为两者没有从属关系。那么想到了private继承。
并且重定义了ontick函数。
class Widget:public Timer { private: virtual ontick() const; ... };
显然,这是可行的,那么使用复合难道就不可以了 吗?显然是可以的,并且鼓励使用复合,只需要在Widget的内部定义一个值属于该类自身的一个类用来继承Timer类,即:
class Widget { private: class TimerManager:public Timer { public: void ontick() const; ... }; TimerManager timer; ... };
这个设计比上述的private继承更易理解。还有一点很有意思,在此处假设 Widget 是意欲成为Base class的类,也就
是说其他的类会继承Widget 如果你使用private继承的话,那么Widget 的derived 类可能会修改了ontick 函数(毕竟这
也是它的基类的虚函数)。
但是,如果需要在该函数中重写Timer中的virtual函数,那么选择private继承就是必须的了。
其次,如果是 private 继承,在定义Widget 类的时候,Timer类必须是可见的,也就是说必须有包含 “Timer.h” 字样,
可是在复合中,完全可以使用 Pointer to Implement (pImpl)技术,在Widget类定义一个TImer的指针即可,这里就
只需要Timer的声明,从而降低了编译的耦合性。
但是对于如下的极端例子:要继承的类是空类
class Empty{}; class HasAInt { private: int a; Empty e; };
但是使用private继承。C++编译器便有一种称作 “ 空白基类最优化的” 原则,是的sizeof(hasAInt) == sizeof(int);
(注:此原则只对单一继承有效),这对于 那些对对象尺寸要求很严格的程序来说,可能很重要。
综上所述:private继承不是没有用的,但是如果可以采用复合实现相同的功能,那就对它敬而远之吧。