C++ 使同构容器中的派生类相互之间的连接不同

C++ 使同构容器中的派生类相互之间的连接不同,c++,polymorphism,dynamic-cast,C++,Polymorphism,Dynamic Cast,我在编写Sokoban游戏时遇到了一个意想不到的问题,当时我不得不处理多态性。 以下是我的简化类层次结构: #include <iostream> #include <vector> #include <memory> using namespace std; class GameObject { public: virtual void onContact(GameObject* otherObject) { cout &

我在编写Sokoban游戏时遇到了一个意想不到的问题,当时我不得不处理多态性。 以下是我的简化类层次结构:

#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;
}
所以我的问题是:作为莫斯塔尔?stl容器是同质的,在我的案例GameObject中,所有派生对象都存储为基本对象,因此根据方法onContact中参数的类型,妨碍了正确的多态性

如何爬回原来的类型,没有动态铸造和检查什么工作的一切和任何地方? 课堂设计有缺陷吗

谢谢你的帮助

TL;DR:如何在相互之间进行不同交互的派生对象的同质集合中优雅地应用多态性


要点:

您需要某种双重分派机制。一种可能是使用访问者模式在两个对象的运行时类型上分派碰撞器

将默认行为放入基类:

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);
  }
};
然后将基类处理程序转发到默认的处理程序,您可以通过重载来执行此操作,这意味着您不需要执行此操作,但这样,如果您添加了一个新类而未向其添加处理程序,则会出现编译错误:

void GameObject::contactHandlerCrate(Crate* o)
{
  contactHandlerDefault(o);
}
//...

现在调用gameObjects[1]->onContactgameObjects[0];推动板条箱的输出。请注意,此方法意味着您需要反转调用,因此您不需要调用hero->onContactcrate,而是调用crate->onContacthero。

多亏了@TartanLlama,我正确地实现了它。 我还了解到,这被称为双重分派:

唯一需要注意的是相反的顺序:

请注意,此方法意味着您需要反转调用,因此您不需要调用hero->onContactcrate,而是调用crate->onContacthero

代码如下:

#include <iostream>
#include <vector>
#include <memory>

using namespace std;

class Crate;
class Wall;
class Hero;

class GameObject
{
public:
    virtual void onContact(GameObject* o)
    {
        o->onContactHandler(this);
    }
    virtual void onContactHandler(GameObject* o)
    {
        cout << "object met object" << endl;
    }
    virtual void onContactHandler(Hero* h)
    {
        cout << "object met hero" << endl;
    }
    virtual void onContactHandler(Wall* w)
    {
        cout << "object met wall" << endl;
    }
    virtual void onContactHandler(Crate* c)
    {
        cout << "object met crate" << endl;
    }
};

class Crate: public GameObject
{
public:
    virtual void onContact(GameObject* o) override
    {
        o->onContactHandler(static_cast<Crate*>(this));
    }
    virtual void onContactHandler(Wall* w) override
    {
        cout << "crate met wall" << endl;
    }
    virtual void onContactHandler(Hero* h) override
    {
        cout << "crate met hero" << endl;
    }
};

class Wall: public GameObject
{
public:
    virtual void onContact(GameObject* o) override
    {
        o->onContactHandler(static_cast<Wall*>(this));
    }
    virtual void onContactHandler(Crate* c) override
    {
        cout << "wall met crate" << endl;
    }
    virtual void onContactHandler(Hero* h) override
    {
        cout << "wall met hero" << endl;
    }
};


class Hero: public GameObject
{
public:
    virtual void onContact(GameObject* o) override
    {
        o->onContactHandler(static_cast<Hero*>(this));
    }
    virtual void onContactHandler(Crate* c) override
    {
       cout << "hero met crate" << endl;
    }
    virtual void onContactHandler(Wall* w) override
    {
        cout << "hero met wall" << endl;
    }
};



int main()
{
    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()); // "crate met hero"
    hero->onContact(wall.get()); // "wall met hero"
    crate->onContact(wall.get()); // "wall met crate"
    wall->onContact(crate.get()); // "crate met wall"

    cout << endl;

    vector<unique_ptr<GameObject>> gameObjects;
    gameObjects.push_back(move(hero));
    gameObjects.push_back(move(crate));
    gameObjects.push_back(move(wall));

    gameObjects[0]->onContact(gameObjects[1].get()); //crate met hero
    gameObjects[2]->onContact(gameObjects[1].get()); //crate met wall

    return 0;
}
谢谢大家!


要点:第二个文件

请发布没有随机错误的内容。我看到这段代码中有许多错误,这些错误与您的要求无关。
#include <iostream>
#include <vector>
#include <memory>

using namespace std;

class Crate;
class Wall;
class Hero;

class GameObject
{
public:
    virtual void onContact(GameObject* o)
    {
        o->onContactHandler(this);
    }
    virtual void onContactHandler(GameObject* o)
    {
        cout << "object met object" << endl;
    }
    virtual void onContactHandler(Hero* h)
    {
        cout << "object met hero" << endl;
    }
    virtual void onContactHandler(Wall* w)
    {
        cout << "object met wall" << endl;
    }
    virtual void onContactHandler(Crate* c)
    {
        cout << "object met crate" << endl;
    }
};

class Crate: public GameObject
{
public:
    virtual void onContact(GameObject* o) override
    {
        o->onContactHandler(static_cast<Crate*>(this));
    }
    virtual void onContactHandler(Wall* w) override
    {
        cout << "crate met wall" << endl;
    }
    virtual void onContactHandler(Hero* h) override
    {
        cout << "crate met hero" << endl;
    }
};

class Wall: public GameObject
{
public:
    virtual void onContact(GameObject* o) override
    {
        o->onContactHandler(static_cast<Wall*>(this));
    }
    virtual void onContactHandler(Crate* c) override
    {
        cout << "wall met crate" << endl;
    }
    virtual void onContactHandler(Hero* h) override
    {
        cout << "wall met hero" << endl;
    }
};


class Hero: public GameObject
{
public:
    virtual void onContact(GameObject* o) override
    {
        o->onContactHandler(static_cast<Hero*>(this));
    }
    virtual void onContactHandler(Crate* c) override
    {
       cout << "hero met crate" << endl;
    }
    virtual void onContactHandler(Wall* w) override
    {
        cout << "hero met wall" << endl;
    }
};



int main()
{
    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()); // "crate met hero"
    hero->onContact(wall.get()); // "wall met hero"
    crate->onContact(wall.get()); // "wall met crate"
    wall->onContact(crate.get()); // "crate met wall"

    cout << endl;

    vector<unique_ptr<GameObject>> gameObjects;
    gameObjects.push_back(move(hero));
    gameObjects.push_back(move(crate));
    gameObjects.push_back(move(wall));

    gameObjects[0]->onContact(gameObjects[1].get()); //crate met hero
    gameObjects[2]->onContact(gameObjects[1].get()); //crate met wall

    return 0;
}