Я пытаюсь понять, как работает двойная отправка. Я создал пример, в котором мог сражаться монстр и воин, полученные из абстрактного класса Существо. Класс Creature имеет метод «бой», который определяется в производных классах, и в каждом производном классе определяется, что происходит, если воин сражается с воином или с монстром и т. Д. Я написал следующий код:
#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 (Существо&) ’: Ex12_10.cpp: 17: 30: ошибка: у класса« Существо »нет члена с именем« fightwho »
ex12_10.cpp: В функции-члене ‘виртуальный void Воин :: бой (Существо&) ’: Ex12_10.cpp: 24: 29: ошибка: у класса« Существо »нет члена с именем« fightwho »
Но я не знаю, как действовать отсюда … Пожалуйста, помогите.
Ну, очевидно, у тебя действительно нет 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)
Следовательно, метод, который в конечном итоге будет вызван, будет отправлен в соответствии с типом объекта, для которого вы вызываете его, и типом объекта, переданного в качестве аргумента, т.е. двойная отправка
Кроме того, обратите внимание, что это может быть не лучшим примером, поскольку ваши типы являются членами одной иерархии. Шаблон дизайна посетителя является хорошим примером реализации двойной диспетчеризации на языках, которые не поддерживают его как граждане первого класса (например, C ++ и производные: Java, C # …)
Как правильно замечает @CrazyCasta, когда ваша иерархия классов начинает расти, этот подход становится намного сложнее поддерживать и может привести к взрыву множества методов, поэтому выбирайте осторожно …
Мой вклад в приведенные выше ответы дает хорошо проверенный пример, чтобы прояснить концепцию двойной отправки в реальности. Если вы посмотрите код ниже, вы найдете ответ как я могу реализовать сам.
#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);
}
Если вы хотите сделать это, вам нужно будет использовать RTTI. Вам нужно будет проверить тип передаваемой вещи. В общем, это не лучший шаблон проектирования, который можно использовать, если вы можете избежать его. Если вы хотите взаимодействовать с двумя объектами, вы обычно хотите использовать стандартный интерфейс другого. Например, вы могли бы сказать, что creature.attack (other_creature) и атака могут запросить защиту другого существа, и на основании этого и его собственной статистики опубликовать обновление hp для other_creature.