2

多重継承を使用して、開発中の複雑な階層を解決しようとしています。状況は次のとおりです。

class A {
  virtual void foo();
}

class B {
  virtual void foo();
}

class C : public B {

}

class D : public A, public C {
  void foo() { ... }
}

class ClientClass {
  void method() {
    A *a = new D();
    a->foo();
  }

私が疑問に思うのは、最終的にはD1つの機能だけになるのでしょうか。foo()メソッドは両方の親で仮想であるため、同じものにコリメートする必要があるため、私はそれについて考えていますが、Javaから来たという理由だけでこれを薄くし、C++では異なる可能性があると感じています。わからない、またはただのため、仮想関数foo()を2回宣言する必要があります。これは私が守りたい要件です。ClientClassBCA

編集:とが純粋な仮想foo()である場合でも、同じ答えが当てはまりますか?(例)AB= 0

4

5 に答える 5

3

Dは2つあります。A :: foo()クラス内およびクラス内でアクセスできますB :: foo()

于 2012-09-13T17:00:45.943 に答える
2

このクラスには、 、のD3 つの関数がありfooます。D::fooB::fooA::foo

は と の両方でfoo仮想であるため、修飾されていない関数呼び出しは、最も派生した関数に動的にディスパッチされます。AB

void f(A & a) { a.foo(); }
void g(B & b) { b.foo(); }

int main()
{
    D d;
    f(d);       // calls d.D::foo();
    g(d);       // calls d.D::foo();

    d.foo();    // calls d.D::foo();
    d.B::foo(); // calls d.B::foo();
    d.A::foo(); // calls d.A::foo();
}
于 2012-09-13T17:33:25.063 に答える
2

実際には、 の実装はと の両方を同時にD::fooオーバーライドします。A::fooB::foo

もちろん、両方の継承された関数は、完全な名前で呼び出すことができます。しかし、それは単一継承の場合と変わりません。

についてはClientClass、オブジェクトを作成し、ポインタを介してDを呼び出します。したがって、オーバーライドが呼び出されます。fooAD::foo

andfooのオーバーライドに異なるバージョンの が必要な場合(たとえば、それらは無関係であるがたまたま同じ方法で呼び出されるため)、少し作業が必要になります。A::fooC::foo

class A2 : public A
{
public:
    virtual void foo()
    {
        A_foo();
    }
    virtual void A_foo()
    {
        A::foo();
    }
};
class C2 : public C
{
public:
    virtual void foo()
    {
        C_foo();
    }
    virtual void C_foo()
    {
        C::foo();
    }
};
class  D: public A2, C2
{
public:
    virtual void A_foo()
    { /* ... */ }
    virtual void C_foo()
    { /* ... */ }
};

今使用:

{
    A *a = new D;
    a->foo(); //will call A2::foo -> D::A_foo
    B *b = new D;
    b->foo(); //will call C2::foo -> D::C_foo
    D *d = new D;
    d->foo(); //error: ambiguous call!
}
于 2012-09-13T17:08:46.377 に答える
1

多重継承に関する古典的な問題にぶつかりました。おそらく最も重要な問題は、(ミックスインまたはインターフェイス/仮想クラスの概念を通じて) 使用が非常に制限されていることを示唆しています。

詳細な説明とさまざまな代替案については、SO のこの投稿をご覧ください。

于 2012-09-13T17:02:00.937 に答える
0

このコードでは:

class ClientClass {
  void method() {
    A *a = new D();
    a->foo();
  }

D の foo が実行されます。また、a->A::foo() を使用して A foo にアクセスすることもできます。'a' を初期化した方法では、B foo() に到達できないことに注意してください。ただし、D インスタンスを作成するたびに、D foo が使用されます。これは、両方の foo 関数が virtual として宣言されているためです。

コード内のクラスごとに、関数のテーブルがあります。各オブジェクトのテーブルは、そのタイプによって定義されます。これは、A * a = new D()関数の A テーブルがあることを意味します。関数の前に仮想フラグがある場合、派生クラスのインスタンスがビルドされている場合、そのテーブルには派生クラスの関数へのポインターが含まれることを意味します。

つまり、ClinetClass のコードでは、「a」オブジェクトには A 関数テーブルがあり、D foo 関数へのポインターを含む foo 関数です。

Dに独自のfooがなく、次のように書く場合に注意してください

D * d =  new D();
d-> foo();

どの foo 関数を使用すればよいかわからない場合、コンパイル エラーが発生します。

于 2012-09-13T17:30:26.680 に答える