理解双重分派C ++(Understanding double dispatch C++)

2019-06-18 07:23发布

我试着去了解如何双重分派工作。 我创建了一个例子,其中一个怪物,并从抽象类生物来源的战士可以战斗。 该类生物具有法“打架”,这是在派生类中定义,并在每个派生类中的定义,如果战士用武士或怪物等我写了下面的代码战斗会发生什么:

#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:在成员函数“虚拟无效妖怪战斗::(生物&)”:ex12_10.cpp:17:30:错误:“类生物”没有名为“fightwho”构件

ex12_10.cpp:在成员函数“虚拟战士空隙::斗争(生物&)”:ex12_10.cpp:24:29:错误:“类生物”没有名为“fightwho”构件

但我不知道如何从这里出发......请帮助。

Answer 1:

嗯,很明显,你真的没有fightwho在你宣称的Creature类,所以您需要在那里声明它,并宣布它作为virtual

双调度工作的方式,呼叫(假设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)

因此,这将最终被调用的方法将根据类型上你怎么称呼它,并将其作为参数的对象类型的对象被派遣,即双重分派

此外,请注意,这可能不是最好的例子作为你的类型相同层次的成员。 Visitor设计模式是双重分派实施的语言很好的例子,它不支持它作为一等公民(即C ++及衍生物:Java和C#...)

作为@CrazyCasta正确地指出,当你的类层次结构开始增长,这种方法变得更难维持,并可能导致的方法数量的爆炸,因此请谨慎选择...



Answer 2:

我上面的回答贡献,以澄清在现实双重分派概念提供完善的测试实例。 如果您查看下面的代码,你会发现我怎么能自己实现了答案。

#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);

}


Answer 3:

如果要做到这一点,你需要使用RTTI。 您将需要检查的传递的东西的类型。一般来说,这是不被使用,如果你能避免它最好的设计模式。 如果你想交流的两个对象,你一般要使用另一人的标准接口。 例如,你可能会说creature.attack(other_creature)和攻击可能查询其他生物的防御,并基于该和它自己的统计数据发布的HP更新到other_creature。



文章来源: Understanding double dispatch C++