条款39、明智审慎地使用private继承

来源:互联网 发布:c语言点滴 pdf 百度云 编辑:程序博客网 时间:2024/05/23 02:12
TK32论证了public继承是is-a关系。类student继承自类Person。于是在编译必要时student暗自转变为Person。现在考虑private继承:
class Person{….};class Student:private Person{….};void eat(const Person& p);      //任何人都会吃void study(const Student& s) ;  //学生专属行为Person p;Student s;eat(p);    //Teat(s);    //F 错误,难道学生不是人?

很明显private继承不是is-a关系。
编译器不会将派生类对象转为基类对象,同时,private基类继承而来的所有成员,在派生类中都会变成private,无论原本在基类中是Protecte或public属性。
现在讨论其意义:private继承意味着就某物实现出。D private B,只是为了采用B内已经备妥的某些特性。私有继承纯粹只是一种实现技术。TK34术语:私有继承意味着只有实现部分被继承,接口部分应略去。D private B,即、D对象根据B对象实现而得。private在软件设计层面上没有意义,意义只在于软件实现层面。Private继承和复合都是根据某物实现出,取舍方法是:尽量使用复合,必要时才使用私有继承。主要是protected成员或虚函数牵扯进来的时候。假设有一个类Widget,因为知道要调用次数而使用Timer类:
class Timer{         public:                   explicit Timer(int  Frequncy);                   virtual void onTick() const;   //定时器每响一次,函数调用一次….};

我们可以重新定义虚函数用以取出Widget当时状态。这样必须继承Timer。但public明显不合适,因为widget并不是一个Timer。那并不是Widget接口的一部分。如果那样调用,会违反TK18“让接口易正确使用不易被调用”。于是我们采用private:
class Widget: private Timer{         private:                   virtualvoid onTick() const;   //查看Widget数据等};

这样onTick函数由公有变为私有函数,我们重新定义时仍把它留在那。
这虽然是个好设计,但私有继承并非绝对必要。我们可以采用复合的方法。如下:
class Widget{         private:         classWidgetTimer: public Timer{         public:         virtual void onTick() const;}WidgetTimer  timer;};

这样做的好处是:
1、Widget可以拥有Timer类,但又能防止派生类改写onTick函数。如果是继承Timer则不可能实现(私有继承也如此),因为派生类总是可以重定义虚函数。WidgetTimer是该类的私有成员,派生类无法取得,这样也就无法定义WidgetTimer的虚函数了。类似于Java的final----“阻止派生类重新定义虚函数
2、降低编译依存性。如果是继承,Widget编译时Timer必须可见,即需要#include”Timer.h”.如果WidgetTimer移出widget,widget内只包含一个指 向WidgetTimer的指针。widger只需带一个简单的WidgetTimer声明,无需include任何与Timer相关的东西。
但涉及一种激进情况或空间最优化时,我们应选择“私有继承”而非“继承加复合”C+规定:独立对象必须具有非零大小。如下:
class Empty{…};  //为空class anInt{         private:         Empty e;   //应该不需要内存         int x;}

你会发现sizeof(anInt)>sizeof(int); 而在大多数编译器中sizeof(Empty)为1.
因为面对“大小为零的独立对象”:C+官方默默安插一个char对象到空对象
但又根据齐位需要,anInt不只是获得一个char大小,而是被放大到int.
但这个约束不适用于派生类的基类成分。如下:
class anInt:private Empty{         private:                  int x;};

这样就保证了sizeof(anInt)=sizeof(int); 这就是EBO(empty base optimization)空白基类最优化。如果在意空间的情况下,可以采用EBO。但注意的是只能是单一继承,无法施行于拥有多个基类的派生类上。
事实上“空”类也不一定是空的,虽然没有非静态成员,但往往有typedef,enum,static成员。如STL中就有许多有用的空类。这样,EBO很少成为private继承的正当理由。复合和私有继承都是is-implemented-in-terms-of. 但只要在可以的情况下,都应选择复合。
但对于两个并不存在is-a关系的类,一个需要访问另一个的protected成员或要重新定义一个或多个虚函数,私有继承可能成为正统方法。
需要记住的:1、私有继承意味着根据某物实现出,通常比复合级别低。但当派生类需要访问protected base的成员或要重新定义继承而来的虚函数时,这么设计是合理的。2、和复合不同,私有继承可以empty base最优化。这对于空间要求高的开发而言很重要。
0 0
原创粉丝点击