继承和列表

Inheritance and lists

本文关键字:列表 继承      更新时间:2023-10-16

我需要管理蚂蚁和蚁群进行一个小游戏(实际上是为了实验)。

我有一个Element类,它定义了游戏中的所有实体(蚂蚁、殖民地、食物和其他东西…)

所有其他类都派生自此类。

我的问题:

我有一个类来管理所有实体。玩家可以选择他想要的东西。选择的实体被存储:Element* selection;如果选择的实体是Ant,玩家可以移动它。但是,因为选择变量是Element指针,我显然不能调用Ant类中的move()方法。

我认为要测试的内容:

如果我实现一个名为isMovable()的Element方法,该方法返回true或false,并且如果选择是可移动的,我会将其强制转换为Ant?我不知道什么是正确的解决方案。

我的移动方法:

void Manager::movementEvent(sf::Vector2i mPosition)
{
    sf::Vector2f mousePosition = sf::Vector2f((float)mPosition.x, (float)mPosition.y);
    if(this->selection) {
        // I need to move the selected Ant
    }
}

感谢您的帮助

编辑

这里是我的实际设计:

class Element {
private:
    sf::Vector2f position;
    int width, height;
public:
    Element();
    Element(sf::Vector2f position, int width, int height);
    Element(const Element & element);
    virtual ~Element();
};
class Colony: public Element {
private:
    int capacity;
    Queen *queen;
public:
    Colony();
    Colony(sf::Vector2f position, int width, int height, int capacity, Queen &queen);
    Colony(Colony const & colony);
    virtual ~Colony();
    Colony& operator=(Colony const& colony);
};
class Ant: public Element
{
private:
    sf::Vector2f destination;
    int number, age, speed;
public:
    Ant();
    Ant(sf::Vector2f position, int number, int age, int width, int height, int speed);
    Ant(const Ant & ant);
    virtual ~Ant();
    Ant& operator=(Ant const& ant);
};
class Manager {
private:
    std::vector<Element*> ants;
    std::vector<Element*> colonies;
    Element* selection;
    std::vector<Ant*> movement;
public:
    Manager();
    virtual ~Manager();
    std::vector<Element*> getAnts();
    std::vector<Element*> getColonies();
    void addAnt(Ant* ant);
    void addColony(Colony* colony);
    void removeAnt(Ant* ant);
    void removeColony(Colony* colony);
    void draw(sf::RenderWindow * window);
    void drawElement(sf::RenderWindow * window, std::vector<Element*> vector);
    void selectionEvent(sf::Vector2i mousePosition);
    bool checkSelection(sf::Vector2f mousePosition, std::vector<Element*> vector);
    void movementEvent(sf::Vector2i mousePosition);
};

我更倾向于避免一般的设计,因为它给我的印象充其量是一种强迫配合。

基类应该定义多个派生类之间的公共行为,并为该公共行为提供公共接口。然而,在这种情况下,在我看来,你的派生类实际上没有常见的行为,所以你在它们之间几乎没有或根本没有有用的公共接口。

在这种情况下,通过强迫它们都从一个(基本上没有意义的)"实体"类派生,你可能会损失比收获多得多。事实上,我建议,几乎任何时候,当你发现自己在思考一个像"对象"或"实体"这样笼统的类名,而这个类名并不能表明一系列有意义的行为时,你很有可能试图把不真正属于一起的东西推到一起。

话虽如此,如果你真的坚持这样做,我会坚持一条基本准则,那就是与其问,不如说。因此,我会在基类中定义一个try_to_move(或者可能只是将其命名为move),但提供了一个失败的默认定义。然后在Ant类中重写它以实际移动。

class Entity { 
// ...
    virtual bool move_to(Location new_location) { 
        return false;
    }
};
class Ant : public Entity { 
// ...
    virtual bool move_to(Location new_location) { 
        my_location = new_location; 
        return true;
    }
};

通过这种方式,您可以告诉Entity派生的任何移动,但如果您告诉Food对象移动,它就会失败。这大大简化了调用代码。而不是像这样的模式

if (object->can_move()) {
    if (object->move_to(new_location))
        // succeeded
    else
        // failed
}

我们得到的代码如下:

if (object->move_to(new_location))
     // succeeded
else
     // failed

至少在一个典型的情况下,即使我们告诉蚂蚁移动,我们也很可能最终处理失败的可能性,所以在要求它移动之前,添加询问物体是否可以移动的元素,实际上对我们没有任何好处。

根据情况,可能想要稍微更改代码,因此移动失败的不同原因会返回不同的错误代码,因此当/如果失败时,您可以找出原因。或者,您可能更喜欢编写代码,以便它要么成功移动,要么抛出。在这些的情况下(你宁愿它至少有一部分时间会失败),这可能不是最好的选择,但可能仍然值得考虑。

然而,我要重申的是,我认为更好的设计可能是将AntFood分开,这样就可以很容易地将食物作为食物,将蚂蚁作为蚂蚁来处理,而不必在运行时区分某个东西是食物还是蚂蚁来知道如何与它交互。

这闻起来真的像是在解决错误的问题。您可以使用isMovable和强制转换等标志使其工作,但您的代码可能会变得一团糟,让您头疼。
也许你的问题实际上是

"我有一个类来管理所有实体"

如果它们没有任何关联,那么它们可能不应该向实体表达Is-A关系。如果每种类型都有不同的容器,可能会更干净。如何将用户想要的操作与"实体"联系起来将是另一回事。

您可以在基类上添加virtual方法move(),而不是仅为Ant类实现它,因此当检查Element是否可移动时,它应该移动:

class Element
{
public:
   Element(bool movable) : m_movable(movable) {}
   virtual void move() {};
   bool isMovable() const { return m_movable; }
private:
   bool m_movable;
};
class Ant : public Element
{
public:
   Ant() : Element(true) {}
   void move() { /* move */ }
};
class Food : public Element
{
public:
   Food() : Element(false) {}
};

通过这种方式,每个派生类都有一个move()方法,事实上,但它是从基类继承的(所以它是空白的)。

编辑

奥卡姆剃刀告诉我们,在这种情况下,您也不需要bool m_movable标志,因此代码片段简化为:

class Element
{
public:
   Element() {}
   virtual void move() {};
};
class Ant : public Element
{
public:
   Ant() {}
   void move() { /* move */ }
};
class Food : public Element
{
public:
   Food() {}
};