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

	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);    //T
eat(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最优化。这对于空间要求高的开发而言很重要。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值