18

ダブルディスパッチがどのように機能するかを理解しようとしています。抽象クラス Creature から派生したモンスターと戦士が戦う例を作成しました。クラスCreatureには、派生クラスで定義されたメソッド「fight」があり、各派生クラスでは、戦士が戦士またはモンスターなどと戦う場合に何が起こるかが定義されています。私は次のコードを書きました:

#include<iostream>
using namespace std;

class Monster;
class Warrior;

class Creature{
public:
    virtual void fight(Creature&) =0;
};

class Monster: public Creature{
    void fightwho(Warrior& w) {cout<<"Monster versus Warrior"<<endl; }
    void fightwho(Monster& m) {cout<<"Monster versus Monster"<<endl; }
public:
    void fight(Creature& c)  {c.fightwho(*this);}
};

class Warrior: public Creature{
    void fightwho(Warrior& w) {cout<<"Warrior versus Warrior"<<endl; }
    void fightwho(Monster& m) {cout<<"Monster versus Warrior"<<endl; }
public:
    void fight(Creature& c) {c.fightwho(*this);}
};

int main()
{
Warrior w;
Monster m;
w.fight(m);
}

これにより、コンパイラ エラーが発生します。

ex12_10.cpp: メンバー関数 'virtual void Monster::fight(Creature&)': ex12_10.cpp:17:30: エラー: 'class Creature' には 'fightwho' という名前のメンバーがありません

ex12_10.cpp: メンバー関数 'virtual void Warrior::fight(Creature&)': ex12_10.cpp:24:29: エラー: 'class Creature' には 'fightwho' という名前のメンバーがありません</p>

しかし、ここから先の進め方がわかりません... 助けてください。

4

3 に答える 3

15

明らかに、実際にはクラスでfightwho宣言していないCreatureので、そこで宣言し、 として宣言する必要がありますvirtual

二重ディスパッチは、 for call のように機能します (これはWarrior& w = ...ではなく を想定していますWarrior w):

w.fight(m);

最初に仮想メカニズムがWarrior::fightの代わりに選択しMonster::fight、次にオーバーロード メカニズムが のMonster::fightwho(Warrior& m)代わりに選択しWarrior::fightwho(Warrior& m)ます。次のものがあれば、より意味があることに注意してください。

Warrior w;
Monster m;
Creature& c1 = w;
Creature& c2 = m;
c1.fight(c2); // not w.fight(m)

したがって、最終的に呼び出されるメソッドは、呼び出すオブジェクトのタイプと引数として送信されたオブジェクトのタイプに応じてディスパッチされます。つまり、二重ディスパッチです。

さらに、型が同じ階層のメンバーであるため、これは最良の例ではない可能性があることに注意してください。ビジター デザイン パターンは、ファースト クラス シチズンとしてサポートされていない言語 (つまり、C++ および派生物: Java、C#...) でのダブル ディスパッチの実装の良い例です。

@CrazyCastaが正しく指摘しているように、クラス階層が成長し始めると、このアプローチは維持するのがはるかに難しくなり、メソッドの数が急増する可能性があるため、慎重に選択してください...

于 2012-09-25T11:34:19.490 に答える
6

上記の回答に対する私の貢献は、実際の二重ディスパッチの概念を明確にするために、十分にテストされた例を提供することです。以下のコードを確認すると、自分で実装する方法の答えが見つかります。

#include <iostream>

using namespace std;

class A;
class A1;
class A2;
class B1;
class B2;

class B {
    public:
        // dispatcher function to A
        virtual void collide(const A& a) const = 0;

        // actual collision logic B with types of A
        virtual void collide(const A1& a) const = 0;
        virtual void collide(const A2& a) const = 0;
};

class A {
    public:
        // dispatcher function to B
        virtual void collide(const B& b) const = 0;

        // actual collision logic A with types of B
        virtual void collide(const B1& b) const = 0;
        virtual void collide(const B2& b) const = 0;
};

class A1 : public A {
    public:
        void collide(const B& b) const {
            // dispatch to b
            b.collide(*this);
        }
        void collide(const B1& b) const {
            cout << "collision with B1 and A1" << endl;
        }
        void collide(const B2& b) const {
            cout << "collision with B2 and A1" << endl;
        }
};

class A2 : public A {
    public:
        void collide(const B& b) const {
            // dispatch to a
            b.collide(*this);
        }
        void collide(const B1& b) const {
            cout << "collision with B1 and A2" << endl;
        }
        void collide(const B2& b) const {
            cout << "collision with B2 and A2" << endl;
        }
};

class B1 : public B {
    public:
        void collide(const A& b) const {
            b.collide(*this);
        }
        void collide(const A1& b) const {
            cout << "collision with A1 Bnd B1" << endl;
        }
        void collide(const A2& b) const {
            cout << "collision with A2 Bnd B1" << endl;
        }
};

class B2 : public B {
    public:
        void collide(const A& a) const {
            a.collide(*this);
        }
        void collide(const A1& a) const {
            cout << "collision with A1 Bnd B2" << endl;
        }
        void collide(const A2& a) const {
            cout << "collision with A2 Bnd B2" << endl;
        }
};

int main() {

    A* a = new A1();
    B* b = new B2();

    // first dispatch is done by polymorphism ( a is resolved as a A1 )
    // second dispatch is done in collide function by the function overloading
    // ( in collide function we are sending A1 to collide function of B )
    a->collide(*b);

}
于 2013-12-17T06:06:10.250 に答える
0

これを行うには、RTTI を使用する必要があります。渡されるもののタイプを確認する必要があります。一般に、回避できる場合、これは使用するのに最適な設計パターンではありません。2 つのオブジェクトを相互作用させたい場合は、通常、別のオブジェクトの標準インターフェースを使用します。たとえば、creature.attack(other_creature) と言うと、attack は他のクリーチャーの防御を照会し、それに基づいて、それ自身の統計が HP の更新を other_creature に投稿します。

于 2012-09-25T11:33:03.423 に答える