7

標準的なダイヤモンドの継承を想像してみてください。クラス A は純粋仮想関数 fx を定義し、クラス B は fx の実装を定義し、クラス C と D は fx に対して何も行いません。クラス D のインスタンスで fx を呼び出そうとすると、fx の実装が 1 つしかないにもかかわらず、「あいまいな関数呼び出し」エラーが発生します。これは、B と C が A から仮想的に継承することで解決できます。それは問題の正しい解決策ですか?仮想継承は、仮想関数テーブルのマージをどのように正確に処理しますか?

A--->B--->D

\--->C------^

4

2 に答える 2

19

... 注意してください、Herb Sutter は、多重継承に関する 3 つの優れた記事(1) ここ(2) ここ、および(3) ここを書きました。彼はここの週の第一人者に有益な記事をたくさん書いています。強くお勧めします ...

まず、あなたの階層が正しいかどうかわかりません。私はそれが次のようなものだと思います:

struct A {
    virtual void F() = 0;
};

struct B : A { void F() { } };
struct C : A { };
struct D : B, C { };

AD型のオブジェクトには 2 つのサブオブジェクトがあるため、D は抽象的です。1 つBは B のラティスを介して具体化され、もう 1 つは を通過するラティス内でまだ抽象的Cです。私はあなたがポインタを持っていると思い、Dを呼び出そうとしますF。はい、あいまいさが生じます。これは、コンパイラが 2Fつの別個のラティスで 2 つの関数を検出するためです。

D -> B::F
D -> C -> A::F

次のようになります。

    F()   F()
     A     A
     |     |
 F() B     C
      \   /
        D 

A から仮想的に導出することで、その状況を正式に修正できます。

struct B : virtual A { void F() { } };
struct C : virtual A { };
struct D : B, C { };

次に、ダイヤモンド継承と呼ばれるこの状況があります。

       F()  
        A
      /   \
 F() B     C
      \   /
        D 

B::Fルックアップを行うと、オーバーライドがあることがわかりますA::FA::Fを介して到達することはできますが、継承された virtualD::C::Aであるため、これはもはやあいまいではありません。A

これが特定の問題の正しい解決策であるかどうか-もちろん、それは確かではありません。ほとんどの場合、クラスから virtual を派生させるよりも優れた方法があります。仮想関数テーブルのマージに関する質問に対して - それは実装に完全に依存しています。GCCは、私が知る限り、virtualDを導出すると、 の仮想テーブル内の 1 つの A インスタンスへのポインタを保持します。

于 2009-01-19T14:02:51.493 に答える
3

それは問題の正しい解決策ですか?

「ダイヤモンド」の継承には問題があり、解決策の正しい説明には少し説明が必要です。Meyers の『 Effective C++』の次の章を読むことをお勧めします。

  • 項目 26、潜在的なあいまいさを防ぐ
  • 項目 43、多重継承を慎重に使用する
于 2009-01-19T13:58:34.043 に答える