C++私有继承和保护继承

来源:互联网 发布:mac装win10好吗 编辑:程序博客网 时间:2024/04/30 09:28
C++私有继承和保护继承
2008年07月4日 |  C/C++, 程序设计 占个座先
声明:如需转载请使用超级链接注明本文地址(http://hellobmw.com/archives/cplusplus-private-and-protected-inheritance.html)及英文原文地址(http://www.parashift.com/c++-faq-lite/private-inheritance.html)。

本文目录
如何表达“私有继承”
“私有继承”和“组合(Composition)”如何相似
我应该优先使用“组合(Composition)”还是“私有继承”
我是否应该把一个私有继承类的指针转化为基类指针
“保护继承”与“私有继承”具有哪些相关性
“私有继承”和“保护继承”有什么使用规则
如何表达“私有继承”
继承时使用 private 关键字,而不是 public。例如:

class Foo : private Bar {
 public:
   ...
 };

“私有继承”和“组合(Composition)”如何相似
私有继承是组合(Composition)的一个语法表达上的变种。例:

Car Has-A Engine 这种关系用简单的组合可以表达为:
class Engine {
 public:
   Engine(int numCylinders);
   void start();                 // Starts this Engine
 };
 
 class Car {
 public:
   Car() : e_(8) { }             // Initializes this Car with 8 cylinders
   void start() { e_.start(); }  // Start this Car by starting its Engine
 private:
   Engine e_;                    // Car has-a Engine
 };
它还可以使用私有继承来表达:
class Car : private Engine {    // Car has-a Engine
 public:
   Car() : Engine(8) { }         // Initializes this Car with 8 cylinders
   using Engine::start;          // Start this Car by starting its Engine
 };
以上两个方案有一些相似之处:

在两个方案里,每个 Car 对象都仅拥有一个 Engine 对象。
客户都无法将 Car* 转换为 Engine*。
两个方案里的 Car 类都有一个 start() 方法用来调用包含的 Engine 对象的 start() 方法。
也有一些不同之处:

当你打算让每个 Car 拥有多个 Engine 时,你需要使用组合(Composition)。
私有继承会引入不必要的多重继承。
私有继承允许成员将 Car* 转换为 Engine*。
私有继承允许访问基类的 protected 成员。
私有继承允许 Car 重载 Engine 的虚函数。
私有继承使得 Car 拥有 start() 方法稍稍简单一些(简单地调用 Engine 的 start() 即可)。
需要注意的是,私有继承通常会用来获得对基类的 protected 成员的访问权,但这并不是一个长远的解决方案。

我应该优先使用“组合(包含)”还是“私有继承”
尽量使用组合,必要时才使用私有继承。

一般情况下,你并不需要访问太多其它类的内部,私有继承就给了你这样的一种能力以及责任。但私有继承并不 evil,只是它的维护过于昂贵,因为它把某人更改了某个东西导致破坏掉你的代码的可能性提高了。

私有继承的一个正统的、长远的使用方法是:当你打算建立一个class Fred 使用 class Wilma 的代码,并且 class Wilma 的代码需要调用你的新类 class Fred 的成员函数。在这种情况下,Fred 调用 Wilma 的 non-virtual 函数,Wilma 调用自身的 pure virtual 函数(它们在 Fred 中被重载)。使用组合很难实现这个目的。

class Wilma {
 protected:
   void fredCallsWilma()
     {
       std::cout <<"Wilma::fredCallsWilma()\n";
       wilmaCallsFred();
     }
   virtual void wilmaCallsFred() = 0;   // A pure virtual function
 };
 
 class Fred : private Wilma {
 public:
   void barney()
     {
       std::cout <<"Fred::barney()\n";
       Wilma::fredCallsWilma();
     }
 protected:
   virtual void wilmaCallsFred()
     {
       std::cout <<"Fred::wilmaCallsFred()\n";
     }
 };

我是否应该把一个私有继承类的指针转换为基类指针
一般来说,不应该。

从私有继承类的成员函数和友员函数来看,派生类与基类的关系是已知的。将 PrivatelyDer* 向上转化为 Base*(或者是 PrivatelyDer& 到 Base&)是安全的,不需要也不建议使用转换。

尽管如此,用户应该避免这种行为。当这种转换是私有继承类的 private 行为时,转换没有任何通知。

“保护继承”与“私有继承”具有哪些相关性
相似点:都允许重载虚函数。都不代表与基类是 Is-A 的关系。

不同点:保护继承允许子类的子类知道继承关系,孙子类实际上暴露有实现细节。这样既有好处,也有代价。好处是孙子类可以利用它与基类的关系。代价是如果子类不潜在地打破进一步派生的类,它就无法改变这种关系。(我翻译地实在太汗了,贴上原文吧:This has both benefits (it allows derived classes of the protected derived class to exploit the relationship to the protected base class) and costs (the protected derived class can't change the relationship without potentially breaking further derived classes). )

“私有继承”和“保护继承”有什么使用规则
拿下面的这几个类为例:

class B                    { /*...*/ };
 class D_priv : private   B { /*...*/ };
 class D_prot : protected B { /*...*/ };
 class D_publ : public    B { /*...*/ };
 class UserClass            { B b; /*...*/ };

没有一个派生类可以访问 B 的 private 部分。
在 D_priv 中,B 的 public 和 protected 部分是 private 的。
在 D_prot 中,B 的 public 和 protected 部分是 protected 的。
在 D_publ 中,B 的 public 部分是 public,protected 部分是 protected,因为 D_publ Is-A B。
UserClass 仅可访问 B 的 public 部分,因为 B 对内部实现进行了封装。
要让 B 的 public 方法在 D_priv 或 D_prot 中也是 public,需要使用 B:: 前缀,例:让 B::f(int, float) 方法在 D_prot 也是 public,应该写为:

class D_prot : protected B {
 public:
   using B::f;  // Note: Not using B::f(int,float)
 };