了解双重调度C++

gar*_*erg 12 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);
}
Run Code Online (Sandbox Code Playgroud)

这导致编译器错误,我预见到:

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'的成员

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

Zde*_*vic 9

好吧,显然,你真的没有fightwho在你的Creature课堂上声明,所以你需要在那里声明它,并声明它virtual.

双重调度以调用方式工作(这假定Warrior& w = ...,不是Warrior w):

w.fight(m);
Run Code Online (Sandbox Code Playgroud)

首先,虚拟机制将选择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)
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 不,这是不可能的,你需要在生物中战斗(战士&)和战斗(怪物&),因此为什么这会很快变得非常讨厌. (2认同)

Val*_*lus 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);

}
Run Code Online (Sandbox Code Playgroud)