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。你需要检查传入的东西的类型。一般来说,如果你可以避免的话,这不是最好的设计模式。如果要与两个对象交互,通常需要使用另一个对象的标准接口。例如,您可能会说 bio.attack(other_creature) 和 attack 可能会查询另一个生物的防御,并基于此和它自己的统计信息发布对 other_creature 的 hp 更新。

于 2012-09-25T11:33:03.423 回答