当我不得不处理多态性时,在编写推箱子游戏时遇到了意外的问题。
这是我的简化的类层次结构:
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class GameObject
{
public:
virtual void onContact(GameObject* otherObject)
{
cout << "object met object" << endl;
}
};
class Crate;
class Wall;
class Hero;
class Crate: public GameObject
{
public:
virtual void onContact(Wall* w)
{
cout << "crate met wall" << endl;
}
virtual void onContact(Hero* h)
{
cout << "crate met hero" << endl;
}
};
class Wall: public GameObject
{
public:
virtual void onContact(Crate* c)
{
cout << "wall met crate" << endl;
}
virtual void onContact(Hero* h)
{
cout << "wall met hero" << endl;
}
};
class Hero: public GameObject
{
public:
virtual void onContact(Crate* crate)
{
cout << "hero met crate" << endl;
}
virtual void onContact(Wall* w)
{
cout << "hero met wall" << endl;
}
};
int main()
{
//Works
auto hero = unique_ptr<Hero>(new Hero());
auto crate = unique_ptr<Crate>(new Crate());
auto wall = unique_ptr<Wall>(new Wall());
hero->onContact(crate.get()); // "hero met crate"
hero->onContact(wall.get()); // "hero met wall"
crate->onContact(wall.get()); // "crate met wall"
wall->onContact(crate.get()); // "wall met crate"
cout << endl;
//Problem: in the program the game objects are stored in a vector (homogeneous container)
vector<unique_ptr<GameObject>> gameObjects;
gameObjects.push_back(move(hero));
gameObjects.push_back(move(crate));
gameObjects.push_back(move(wall));
/*
"object met object".
Should be "hero met crate".
That's because all objects in vector are GameObject.
*/
gameObjects[0]->onContact(gameObjects[1].get());
/* "object met object", should be "wall met crate" */
gameObjects[2]->onContact(gameObjects[1].get());
return 0;
}
因此,这是我的问题/问题:由于大多数(all?)stl容器是同质的,所有派生对象都存储为基础对象(在我的情况下为GameObject),因此根据onContact方法中参数的类型,阻碍了适当的多态性。
如何在不进行动态转换(并检查是否起作用)的情况下以及所有位置无处恢复到原始类型?
班级设计有缺陷吗?
谢谢您的帮助。
TL; DR:如何在同质的派生对象的集合中应用优美的多态性而彼此交互地进行交互?
要点:https://gist.github.com/gaultier/e437877395b5831a0623
最佳答案
您需要某种双调度机制。一种可能性是使用访客模式在两个对象的运行时类型上分配对撞机。
将默认行为放入您的基类中:
class GameObject
{
public:
virtual void contactHandlerDefault(GameObject* otherObject)
{
cout << "met an other object";
}
virtual void contactHandlerCrate(Crate* o);
virtual void contactHandlerWall(Wall* o);
virtual void contactHandlerHero(Hero* o);
virtual void onContact(GameObject* otherObject)
{
otherObject->contactHandlerDefault(this);
}
};
然后,对于每个派生类,重写所需的任何碰撞处理程序,然后使它们将
this
指针转换为正确的静态类型,然后将自身转发给碰撞对象中的正确处理程序:class Hero: public GameObject
{
public:
virtual void onContact(GameObject* otherObject) override
{
otherObject->contactHandlerHero(static_cast<Hero*>(this));
}
virtual void contactHandlerDefault(GameObject* obj) override
{
cout << "do nothing, I can't go through";
}
virtual void contactHandlerCrate(Crate* crate) override
{
cout << "pushing the crate";
}
};
然后将基类处理程序转发到默认处理程序(您可以通过重载来执行此操作,这意味着您不需要执行此操作,但是如果您添加新类并且不添加,则这样会出现编译错误处理程序):
void GameObject::contactHandlerCrate(Crate* o)
{
contactHandlerDefault(o);
}
//...
现在调用
gameObjects[1]->onContact(gameObjects[0]);
输出pushing the crate
。请注意,此方法意味着您需要回叫,因此您可以调用hero->onContact(crate)
而不是调用crate->onContact(hero)
。