C++学習ノート19-多重継承と虚継承

5539 ワード

概要:多重継承は、複数の直接ベースクラスからクラスを派生させる能力であり、多重継承の派生クラスはすべての親クラスの属性を継承します.
一、多重継承の定義
     1. カンマで区切られたベースクラスのリスト:
    class Panda : public Bear, public Endangered {
    };

派生クラスは各ベースクラスです(明示的または暗黙的に)アクセス・レベル(public、protectedまたはprivate)が指定されています.単一継承のように、クラスは定義後にのみ多重継承のベース・クラスとして使用できます.クラスが継承できるベース・クラスの数については、言語的に強く制限されていませんが、特定の派生リストでは、1つのベース・クラスが1回しか表示されません.
     2.多重継承派生クラスは、各ベースクラスからステータスを継承します.多重継承の下で、派生クラスのオブジェクトには、各ベースクラスのベースクラスサブオブジェクトが含まれます.
     3.派生クラスコンストラクション関数は、すべてのベースクラスを初期化します.派生タイプを構築するオブジェクトには、すべてのベースクラスサブオブジェクトの構築と初期化が含まれます.単一のベースクラスを継承する場合と同様に、派生クラスのコンストラクション関数は、コンストラクション関数初期化式でゼロまたは複数のベースクラスに値を渡すことができます.マルチ継承でベースクラスのコンストラクタが明示的に呼び出されていない場合、コンパイラはベースクラスのデフォルトコンストラクタを呼び出します.
     4.構築の順序:構築関数の初期化式は、ベースクラスを初期化するための値のみを制御し、ベースクラスの構築順序を制御できません.ベースクラスコンストラクション関数は、クラス派生リスト内のベースクラスコンストラクション関数の出現順に呼び出されます.
    5.コンストラクション関数呼び出し順序は、コンストラクション関数初期化リストに表示されるベースクラスの影響も、コンストラクション関数初期化リストにおけるベースクラスの出現順序の影響も受けません.
6.構造の順序:構造関数が常に実行される逆順序で構造関数を呼び出します.
 
二、変換と複数のベースクラス
     1.単一のベースクラスの場合、派生クラスのポインタまたは参照は、ベースクラスのポインタまたは参照に自動的に変換され、多重継承の場合も、派生クラスのポインタまたは参照は、任意のクラスのポインタまたは参照に変換されます.
     2.多重継承の場合,二義的変換に遭遇する可能性がより大きい.コンパイラは派生クラス変換に基づいてベースクラス間の変換を区別しようとせず、各ベースクラスに変換してもよい.たとえばprint関数のリロードバージョンがある場合:
    void print(const Bear&);
    void print(const Endangered&);
   1.      ,                  (   )   ,             。
 2.              ,             ,                     。
 3.           :                        ,  ,              ,             。
 
 、            :
   1.                、     ,           ,             、              、         。
  2.         ,                    ,             。                       ,      (  )        。                        ,            。
 
 、          :
   1.            ,                。                         ,                。
 2.                ,      。   ,                            ,     。
   3.       :                :
    ying_yang.Endangered::print(cout);

     4.潜在的な二義性を回避する最善の方法は、二義性を解決する派生クラスで関数のバージョンを定義することである.たとえば、どのprintバージョンを使用するかを選択するPandaクラスにprint関数を与えます.
    std::ostream& Panda::print(std::ostream &os) const

    {
        Bear::print(os);        // print the Bear part
        Endangered::print(os);  // print the Endangered part
        return os;
    }
 
 、   :      ,                。   ,                          。

      1.C++では,このような問題を虚継承を用いて解決する.虚継承は、クラスが虚継承によって虚ベースクラスの状態を共有したいことを示すメカニズムです.「≪虚継承|虚継承|虚継承|emdw≫」で、派生階層で虚ベースクラスとして何度出現しても、共有ベースクラスのサブオブジェクトは1つだけ継承されます.共有されるベースクラスサブオブジェクトを虚ベースクラスと呼びます.
      2.istreamクラスとostreamクラスは、それらのベースクラスを虚継承します.ベースクラスをダミーベースクラスにするには、istreamとostreamで指定します.iostreamなどの他のクラスが両方継承されている場合、派生クラスには共通ベースクラスのコピーが1つしか表示されません.派生リストにキーワードvirtualを含めてダミーベースクラスを設定します.
    class istream : public virtual ios { ... };
    class ostream : virtual public ios { ... };
    // iostream inherits only one copy of its ios base class
    class iostream: public istream, public ostream { ... };
  3.      :       virtual     ,             。  ,       ZooAnimal     Bear    Raccoon
    // the order of the keywords public and virtual is not significant
    class Raccoon : public virtual ZooAnimal { /* ... */ };
    class Bear : virtual public ZooAnimal { /* ... */ };
  4.                     。              ,                         。virtual
  5.                     ,                      。
  6.          :        ,                         。
 

    1. Xという名前のメンバーを複数の派生パスで継承すると仮定すると、次の3つの可能性があります.
a.各パスにおいてXが同じダミーベースクラスのメンバーを表す場合、そのメンバーの単一のインスタンスが共有されるため、二義性はない.
b.あるパスにおいてXがダミーベースクラスのメンバーであり、別のパスにおいてXが子孫派生クラスのメンバーである場合、二義性もない.特定の派生クラスインスタンスの優先度は共有ダミーベースクラスインスタンスより高い
c.各継承パスXに沿って子孫派生クラスの異なるメンバーを表す場合、そのメンバーの直接アクセスは二義的である.
非虚多重継承階層のように、この二義性は、派生クラスにインスタンスを上書きするクラスを提供することで解決することが望ましい.
 
八、特殊な初期化の意味
    1.通常、各クラスは自分の直接ベースクラスのみを初期化します.仮想ベースクラスに適用される研修では、この初期化ポリシーは失敗します.通常のルールを使用すると、仮想ベースクラスが複数回初期化される可能性があります.クラスは、ダミーベースクラスを含む各継承パスに沿って初期化されます.この繰返し初期化問題を解決するために,ダミーベースクラスを持つクラスから継承されたクラスから初期化を特殊に処理する.虚派生では、最下位層派生クラスの構造関数によって虚ベースクラスが初期化されます.
   2.最下位レベルの派生クラスによってダミーベースクラスが初期化されますが、ダミーベースクラスを直接または間接的に継承するクラスは、通常、そのベースクラスに独自の初期化式を提供する必要があります.仮想ベースクラス派生クラスタイプの独立したオブジェクトを作成できる限り、クラスは独自の仮想ベースクラスを初期化する必要があります.これらの初期化式は、中間タイプのオブジェクトを作成する場合にのみ使用されます.
 
九、虚継承の対象をどのように構築するか
    1.ダミーベースクラスが継承階層のどこに存在するかにかかわらず、常に非ダミーベースクラスを構築する前にダミーベースクラスを構築します.
a.宣言順に直接ベースクラスを検査し、虚ベースクラスが存在するかどうかを確定する.ダミーベースクラスが構築すると、宣言順に非ダミーベースクラスの構築関数が呼び出される.
b.合成レプリケーション構造関数では同様の構造順序が用いられ、合成付与オペレータでもこの順序でベースクラスに付与される.ベースクラスの構造関数を呼び出す順序が、構造関数の呼び出し順序とは逆であることを保証します.