7

のインターフェイスの多重継承に、私は次のようにテストコードを作った:私はこのコードを書いた理由は、関数funcBase()はDDerivedのインスタンスで呼び出すことができるかどうかをテストすることである曖昧はC++

#include <iostream> 
using namespace std; 

#ifndef interface 
#define interface struct 
#endif 

interface Base 
{ 
    virtual void funcBase() = 0; 
}; 

interface Derived1 : public Base 
{ 
    virtual void funcDerived1() = 0; 
}; 

interface Derived2 : public Base 
{ 
    virtual void funcDerived2() = 0; 
}; 

interface DDerived : public Derived1, public Derived2 
{ 
    virtual void funcDDerived() = 0; 
}; 

class Implementation : public DDerived 
{ 
public: 
    void funcBase() { cout << "base" << endl; } 
    void funcDerived1() { cout << "derived1" << endl; } 
    void funcDerived2() { cout << "derived2" << endl; } 
    void funcDDerived() { cout << "dderived" << endl; } 
}; 

int main() 
{ 
    DDerived *pObject = new Implementation; 
    pObject->funcBase(); 

    return 0; 
} 

又はない。このコードをコンパイルしようとすると、私のC++コンパイラ(Visual Studio 2010)からコンパイルエラーメッセージが表示されました。私の意見では、このコードには問題はありません。なぜなら、それは純粋な仮想であるため、DDerivedインターフェイスの一部の派生クラスでは、関数funcBase()が実装されている(オーバーライドされている)からです。換言すれば、タイプImplementation *のポインタ変数は、Implentationを導出し、関数funcBase()を無効にするクラスのインスタンスに関連付けられるべきである。

私の質問は、なぜコンパイラが私にそのようなエラーメッセージを与えるのですか?なぜC++構文がそのように定義されているのですか?すなわち、この場合をエラーとして扱うか?コードを実行するにはどうすればよいですか?私はインターフェイスの複数の継承を許可したい。私は

interface DDerived : public Derived1, public Derived2 
{ 
    virtual void funcBase() = 0; 
    virtual void funcDDerived() = 0; 
}; 

ようImplementationに「仮想public」または再宣言する機能funcBase()を使用する場合はもちろん、その後、すべてが問題なく動作します。

しかし、仮想継承はパフォーマンスを低下させる可能性があり、クラスの継承関係が非常に複雑な場合には再宣言が非常に面倒なので、より便利なメソッドを探していません。仮想継承を使用する以外に、C++でインタフェースの多重継承を有効にする方法はありますか?

+0

を持っている私の霊魂力は、エラーが曖昧な基底クラスの関数への呼び出しであることを教えてください。 –

+0

しかし、それは基本クラスではなく、基本インターフェースまたは基本構造のいずれかです。 –

+0

[C++多重継承の可能な複製 - なぜあなたは働かないのですか?](http://stackoverflow.com/questions/5864466/c-multiple-inheritance-why-you-no-work) –

答えて

3

C++言語は、仮想継承を持たない最初のアプローチでは、メソッドの親コピーが2つあり、どのメソッドを呼び出すのか把握できないように設計されています。

仮想継承は、複数の拠点から同じ関数を継承するC++ソリューションです。そのため、このアプローチを使用することをお勧めします。

複数の拠点から同じ機能を継承していないと考えていますか?コンテキストによっては、Derived1またはDerived2またはBaseとして扱えるようにする必要がある派生クラスが本当にありますか?

この場合、設計された例ではなく具体的​​な問題を精緻化することで、より良い設計が可能になります。

+1

私は同意できません。 OPが実際のプログラムを投稿する大部分のケースでは、人々はSSCCEを投稿するべきであると叫び始めます。これは、OPが行うまれなケースの1つです。その後、あなたは_that._ –

+0

@ Mr Listerについて文句を言っています。残念ながら、この場合、 'Base'、' Derived'、 'Derived2'が' DDerived'で直接継承されたすべての直交クラスではない理由を説明するのに十分なコンテキスト(十分に単純化されたコード)はありません。 –

1
DDerived *pObject = new Implementation; 
pObject->funcBase(); 

これは、実装にDDerivedタイプのポインタを作成します。 DDerivedを使用しているときは、実際にはインターフェースへのポインタしかありません。

DDerivedはfuncBaseをDerived1とDerived2の両方で定義するというあいまいさのために、funcBaseの実装について知らない。

これは本当に問題を引き起こしている継承ダイヤモンドを作成しました。

http://en.wikipedia.org/wiki/Diamond_problem

私はまた、あなたがそこに

で、それはあなたがそれを定義してきたようにVisual Studioの

7

が認識していますMS-固有の拡張機能の持つインターフェース「キーワード」をチェックしなければなりませんでした

enter image description here

ここで重要なポイント:あなたのオブジェクトの構造は、次のようになりますImplementationの各インスタンスには、完全に別々の2つのインスタンスBaseが含まれています。 Base::funcBaseのオーバーライドを提供していますが、Derived1を継承したBaseの場合はfuncBaseDerived2を継承した場合はBaseをオーバーライドしようとしているかどうかはわかりません。

はい、これに対処する明確な方法は仮想継承です。これは、ほぼ間違いなく、あなたが本当に欲しいものである

enter image description here

:ベースのインスタンスが1つだけありますので、これはあなたの構造を変更します。はい、それは、プリミティブコンパイラと25 MHz 486などの時代にはパフォーマンス上の問題で評判がありました。現代のコンパイラとプロセッサでは、となる可能性は低い問題が発生します。

もう1つの可能性は、テンプレートベースの代替案ですが、それは残りのコードに広がる傾向があります。つまり、Base *を渡す代わりに、関数A、 B、Cを受け取り、Implementationをテンプレートパラメータとして渡します(これと等価)。

+0

お返事ありがとうございます。 「Derived1を継承したBaseまたはDerived2を継承したBaseのfuncBaseを上書きしようとしているかどうかはわかりませんが、そうではないと思います。私が "DDerived * pObject"を "Implementation * pObject"に置き換えたとき、コンパイラはこれ以上不平を言っていませんでした... – user1361349

+0

funcBaseの2つのコピーが実装で与えられた同じメソッドによって実際にオーバーライドされることがあります。同様に、私が私の質問で言ったように、私が "void funcBase()= 0"をDDerivedの定義に追加したとき、コンパイルエラーはありませんでした。あれについてどう思う?ありがとうございました。 – user1361349

1

私はC++標準10.1.4 - 10.1.5がコードの問題を理解するのに役立つと思います。

class L { public: int next; /∗ ... ∗/ }; 
class A : public L { /∗...∗/ }; 
class B : public L { /∗...∗/ }; 
class C : public A, public B { void f(); /∗ ... ∗/ }; 

10.1.4がキーワード仮想が含まれていない基本クラス指定子、 は非仮想基本クラスを指定します。 にキーワードvirtualが含まれている基本クラス指定子は、仮想基本クラスを指定します。 最も派生したクラスのクラス格子 に非仮想基本クラスが別々に出現する場合、最も派生したオブジェクト(1.8)には対応する別個の基本クラスサブオブジェクトである が含まれます。バーチャルで指定された別個の基本クラス ごとに、最も派生したオブジェクト は、そのタイプの単一の基本クラスサブオブジェクトを含むものとする。 []例: クラスタイプCのオブジェクトの場合、Cのクラスラティスの (仮想ではない)基本クラスLの各別個の出現は、オブジェクト内の別個のLサブオブジェクトを伴う に1対1に対応する。 上記で定義されたクラスCが与えられた場合、クラスCのオブジェクトは、以下に示すようにクラスの2つの サブオブジェクトを持つことになります。

10.1。このような格子では、明示的資格を使用して、サブオブジェクトがどのオブジェクトを意味するかを指定することができます。関数C :: fの本文は、各Lサブオブジェクトの次のメンバー を参照することができます。void C :: f(){A :: next = B :: next; } // 整形式。 pObject-> funcBaseを呼び出すときにA ::またはB ::修飾子がなければ、Cの定義::上記 fがが原因曖昧

の悪口-形成されるだろうだから(修飾子を追加します)別の方法であいまいさを解決することができます。

pObject->Derived1::funcBase(); 

更新:も非常に役立つ読書は10.3仮想関数標準のになります。

が素敵な週末:)

関連する問題