C++ 明智而审慎地使用私有(private)继承

20180327 C++ 明智而审慎地使用私有(private)继承


在C++中,会将公有(public)继承视为 is-a 的关系。
当以私有(private)继承替换公有(public)继承的时候,比如:
class Person{...};    
class Student:private Person{...};//私有继承Person类
void eat(const Person& p); //任何人都会吃
void study(const Student& s);//只有学生才会在学校学习


Person p;   //p是人
Student s;  //s是学生


eat(p);   //没问题,p是人,会吃
eat(s);   //错误,难道学生不是人!?


很显然,私有(private)继承并不意味着 is-a 的关系,那它意味着什么呢?


在私有(private)继承中,若类之间的继承关系是私有的,编译器不会自动将一个派生类对象(如Student)转换成一个积累对象(如Person),这就是为什么通过s调用eat会失败的原因了;第二条规则是,由私有基类(private base class)继承而来的所有成员,在派生类中都会变成私有属性,即使它们在基类中原本是保护(protected )或公有(public)属性。


现在我们具体讨论其意义。私有(Private)继承意味着 根据某物实现出(implemented-in-terms-of),若你想让D类以私有形式继承B类,你的用意是为了采用B类内已经备妥的某些特性,不是因为B对象和D对象存在有任何观念上的关系。
私有继承意味着只有实现部分被继承,接口部分应略去,若D以私有形式继承B,意思是D对象根据B对象实现而得,再没有其他意涵,私有继承在软件“设计”层面没有意义,只在软件实现层面有意义。


私有继承意味 根据某物实现出(implemented-in-terms-of),而复合(composition)的意义也是如此,这两者的取舍关系是:尽可能使用复合,必要时才使用私有继承,这里的必要是指 当保护(protected)成员或虚(virtual)函数牵扯进来的时候。


私有继承主要用于“当一个派生类者想要访问基类者的保护(protected)成分”,互为了重新定义一个或多个虚函数,但这时两个类之间的概念关系是 根据某物实现出(implemented-in-terms-of),而非is-a的关系,eg:
//定时器类
class Timer{
public:
  explicit: Timer(int tickFrequency);
  virtual void onTick() const;//定时器每滴答一次,此函数就被自动调用一次
  ... 
};


Widget以私有形式继承Timer:
class Widget:private Timer{
private:
  virtual void onTick() const;
  ...
}


以上设计方法可以由复合取而代之:
class Widget{
private:
  class WidgetTimer:public Timer{
    public :
      virtual void onTick() const;
      ...
  };
  WidgetTimer timer;
  ...
}


另一种情况是涉及空间最优化的,这就让你选用“私有继承”,而非“继承加复合”了。
这种只适合于所处理的类不带任何数据的,这样的类没有非静态成员变量,没有虚函数,也没有虚基类,eg:
class Empty{};//没有数据,所以其对象应该不使用任何内存


class HoldsAnInt{//应该只需要一个int空间
private:
  int x;
  Empty e;   //应该不需要任何内存
};




而令人惊奇的是 sizeof(HoldsAnInt) > sizeof(int);在大多数编译器中sizeof(Empty)是1,因为对于“大小为0的独立(非附属)对象”,通常C++官方要默默安插一个char到空对象中,然而根据对齐原则(又叫 齐位需求,alignment),可能造成编译器为类似HoldsAnInt这样的类加上一些补位(又叫 衬垫,padding),所以有可能HoldsAnInt对象不只获得一个char大小,也许实际上被放大到足够又放进一个int,,在各个编译器中,的确有这种情况。




复合和私有继承都意味着   根据某物实现出(implemented-in-terms-of),但复合比较容易理解,所以无论什么时候,只要可以,请尽量使用复合。






注意:
1)私有继承意味着 根据某物实现出(implemented-in-terms-of)它通常比复合(composition)的级别更低,但是当派生类需要访问基类的保护成员的时候,或者需要重新定义继承而来的虚函数时,这么设计是合理的。
2)和复合(composition)不同,私有继承可以造成空基类(empty base)最优化,这堆致力于“对象尺寸最小化”的程序库开发者而言,可能很重要。



猜你喜欢

转载自blog.csdn.net/weixin_39089680/article/details/79930276