所以
我正在为学校开展一个小型游戏项目,我遇到碰撞,并且我的对象正在工作,我的想法是检查一个对象是否与另一个碰撞,然后让每个类型的特定逻辑都有一堆重载功能取决于发送的对象。例如,如果对象是玩家控制的对象,敌人会伤害它,但如果它是与敌人碰撞的通电,那么事情就会没事。
所以,我希望我可以做类似的事情(一切都从Obj继承):
std::vector<Obj*> list;
list.push_back(new Powerup());
list.push_back(new Enemy());
list.push_back(new Player());
for (auto i: list) {
for (auto j: list) {
if (collision(i,j)) {
i->doStuff(*j);
}
}
}
但我找不到合适类型的发送方式。我做了一个测试程序来证明这个问题:
#include <iostream>
class A {
public:
virtual void doStuff(A& T) { std::cout << "A->A!" << std::endl; }
};
class B : public A {
public:
virtual void doStuff(A& T) { std::cout << "B->A!" << std::endl; }
};
class C : public A {
public:
virtual void doStuff(A& T) { std::cout << "C->A!" << std::endl; }
virtual void doStuff(B& T) { std::cout << "C->B!" << std::endl; }
};
int main() {
A* base;
A a;
B b;
C c;
c.doStuff(a);
c.doStuff(b);
base = &a;
c.doStuff(*base);
base = &b;
c.doStuff(*base);
return 0;
}
运行它我得到了这个:
C->A!
C->B!
C->A!
C->A!
我期待的时候:
C->A!
C->B!
C->A!
C->B!
知道如何使这项工作吗?
答案 0 :(得分:3)
您在此处尝试的内容称为 double dispatch - 使函数虚拟化为两个参数。与大多数具有虚函数的编程语言一样,C ++本身并不支持这种语言。
考虑c.doStuff(*base);
电话。它确实有两个参数:c
(在函数内部最终为*this
)和*base
。
现在问题是doStuff
仅对第一个参数是虚拟的。这就是为什么即使C
具有静态基类类型,调用也会以doStuff
的{{1}}函数之一结束。但是,第二个参数不是以多态方式处理的。静态类型c
是base
,即使它当前指向子类对象,因此A*
会生成*base
,并且与A
重载相匹配
虽然C ++不支持双重调度,但有一些方法可以模拟它。访客设计模式通常被认为是一种方法。例如,请参阅Difference betwen Visitor pattern & Double Dispatch。
P.S。:混合覆盖与重载很少是一个好主意,因为作为代码的人类读者,找出哪个函数将被调用会变得非常混乱。
答案 1 :(得分:0)
问题是,您的上次通话doStuff
传递了A&
,因此会拨打C::doStuff(A&)
。如果您想要拨打C::doStuff(B&)
,则需要将base
投射到B&
。这可以通过static_cast
和dynamic_cast
来完成。
你应该看看Double Dispatch:Understanding double dispatch C++
答案 2 :(得分:0)
问题是C ++使用单一调度,你想要的是双重调度。一种说法是单个调度使用编译时可用的信息(您将类型A的指针传递给函数)而不是运行时已知的信息(类型A的指针恰好指向类型B的对象)所以你想在决定要调用的函数的重载时调用B的重载。
有一些很好的资源:http://members.gamedev.net/sicrane/articles/dispatch.html和 https://en.wikipedia.org/wiki/Double_dispatch
单一调度编程语言通常用于解决此问题,无需投射正在使用&#34;访问者模式&#34;。
class DoStuffClass{ //visitor class
public:
void doStuff(B &b);//traditionally visit(B &b)
void doStuff(C &c);
void doStuff(A &a);
};
class A{ //visited class
public:
void interactWith(DoStuffClass &dsc){
//here's the good part:
//Now it is known at compile time(statically) what the type of *this is,
//so dispatching to the desired function - doStuff(A&) - can happen
doc.doStuff(*this);
};
}
class B: public A{ //visited class
public:
void interactWith(DoStuffClass &dsc){
//same as for A
doc.doStuff(*this);
};
}
注意:传统上,Visitor类成员类称为visit(SomeVisitedClass&amp;),而访问类成员函数称为receiver(Visitor&amp;)。但是你可以随意调用它。
然后你可以这样做:
DoStuffClass dsf;
A a;
B b;
A *aptr;
aptr = &b;
a.interactWith(dsf); //will call the do stuff function that handles A
b.interactWith(dsf); //will call the do stuff function that handles B
aptr->interactWith(dsf);//still calls the do stuff function that handles B
现在,当你了解它时,你也可以看一下Mediator Pattern。它旨在减轻需要了解彼此工作的类之间的依赖关系(如在您的情况下),并使您不必让每个类知道所有其他类: