首页 > 代码库 > 慎用私有继承
慎用私有继承
在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);
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
}
};
---------------------------------------
总结:私有继承成为了一种设计策略,因为只有继承才能访问保护成员,也只有继承才能使虚函数可以重新被定义,而组合就显得力不从心了。