首页 > 代码库 > 慎用私有继承

慎用私有继承

在C++中,public 公有继承被视为”is-a“关系。

class Animal{

public :

   void Eat() { ... }

};

class Tiger : public Animal{

public:

   bool IsKing() {...}

};// Tiger 是一种Animal

如果public换成private,编译器不能将派生类对象转型为基类对象,所有不能调用基类中的成员函数,不再是"is-a"的关系。

----------------------------------------

私有继承表达的含义:私有继承会使基类的所有东西(包括所有的成员变量与成员函数)在派生类中变成 private(私有) 的,即基类的全部在派生类中都只能最为实现细节,而不能成为接口。表达类之间"根据...而实现"关系的唯一有效途径。

私有继承和组合(Composition)相似。著名的Car Has-A Engine例子:

class Engine{

public:
    Engine(int numCylinders);
    void Start(); //Starts this Engine
};
class Car{
public :
    //Initializes this Car with 8 cylines
    Car() : m_engine(8) {  }

    //Move this Car by starting its Engine
    void Move(){
        m_engine.Start();
    }   
private :
    Engine m_engine;  //Car has-a Engine
};

使用私有继承来进行表达:

class Car : private Engine{

      //Car has-a Engine
public :
    //Initializes this Car with 8 cylines
    Car() : m_engine(8) {  }
    //Move this Car by starting its Engine
    void Move(){
        Engine::Start();
    }   
};

//大多数情况下,组合是值得推荐的。因为通常不需要访问其他类太多的内部细节,但是私有继承却给了这样的能力,并且要承担昂贵的维护成本。故采用组合方式在概念上更容易理解。那么,请尽量使用组合,必要时才使用私有继承

---------------------------------------

"必要时"主要包括两种情况(而不是仅仅):

1)当派生类需要访问基类保护成员时

如果处于某种考虑,不想别人去调用类 Engine 中的Start函数,所有将其声明为protect。

class Engine{

public:
    Engine(int numCylinders);

protected:

    void Start(); //Starts this Engine
};

//私有继承可以完成在Car中通过Engine的Start来启动Car的目的:既能正确地表达概念,又能获得在一个类中调用另一个类"保护成员"的能力。

2)需要重定义继承来的虚函数时

假设已经有了一个功能完善的时钟类CTimer:

class CTimer{

public:

  explict CTimer(int frequency);

  virtual void onTick() const;

  //...

};

如果想借助CTimer实现游戏类CGame的Tick,那么重写函数OnTick是在所难免的。因为CGame is a CTimer 在概念上不成立,所以公有继承应否决。则可采用私有继承:

class CGame : private Timer{

public:

  virtual void onTick() const{

    Timer::onTick();

    ...//Other processing codes

  }

};

---------------------------------------

总结:私有继承成为了一种设计策略,因为只有继承才能访问保护成员,也只有继承才能使虚函数可以重新被定义,而组合就显得力不从心了。