了解双重调度C ++

时间:2012-09-25 11:29:49

标签: c++ double-dispatch

我试着了解双重调度的工作原理。我创建了一个例子,其中一个怪物和一个来自抽象类生物的战士可以战斗。类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&amp;)':ex12_10.cpp:17:30:错误:'class Creature'没有名为'fightwho'的成员

ex12_10.cpp:在成员函数'virtual void Warrior :: fight(Creature&amp;)':ex12_10.cpp:24:29:错误:'class Creature'没有名为'fightwho'的成员

但我不知道如何从这里开始......请帮忙。

3 个答案:

答案 0 :(得分:8)

很明显,你的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)

因此,最终调用的方法将根据您调用它的对象的类型和作为参数发送的对象的类型进行调度,即 double dispatch

此外,请注意,这可能不是最佳示例,因为您的类型是同一层次结构的成员。访问者设计模式是语言中双重调度实现的一个很好的例子,它不支持它作为一等公民(即C ++和衍生物:Java,C#......)

正如@CrazyCasta正确指出的那样,当您的类层次结构开始增长时,这种方法变得更难维护并且可能导致方法数量激增,因此请谨慎选择......

答案 1 :(得分: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);

}

答案 2 :(得分:0)

如果你想这样做,你需要使用RTTI。您需要检查传入的东西的类型。一般来说,如果您可以避免它,那么这不是最好的设计模式。如果要交互两个对象,通常需要使用另一个对象的标准接口。例如,你可能会说creature.attack(other_creature)和攻击可能会查询另一个生物的防御,并根据它和它自己的统计数据发布对其他生物的hp更新。