繁体   English   中英

使用纯粹的多态性和继承来调用基类派生类上的函数而不进行强制转换?

[英]Using purely polymorphism and inheritence to call functions on a derived class from base class without casting?

我的问题是:我有一个纸牌游戏,它由小兵、法术和装备组成,这些都是卡的类型,我知道方法的虚拟定义。 我需要使用GetHealth() RemoveHealth()AddHealth()在功能Minion ,但我不希望这些在SpellEquipment 我希望做的是:

class CCard
{
//Member variables and functions
};

class CMinion : public CCard
{
    //Member variables and functions
    int GetHealth() const {return mHealth;}
};

std::unique_ptr<CCard> minion = std::make_unique<CMinion>(params);
minion->GetHealth(); //I realize this isn’t possible the way it is.

我目前有一个单独的与卡片相关联的随从列表和 ID,当卡片在我手中时,我将 Minion 对象放在桌子上而不是卡片上。 我的讲师想要纯粹的多态性和继承性,所以这是一个笨拙的解决方案。 我还讨论了将GetHealth()和其他必需方法放在 Card 类中并使不需要它的类返回 0。他说“这是一个更好的解决方案,但我还是应该使用继承和多态性”。

他认为我的架构是错误的,我应该对其进行重组。

我应该有: 卡片向量是MinionSpellEquipment的实例。 桌子、手和牌应该都是卡片的向量,但我目前的解决方案是牌和手是卡片的向量,而我的桌子是小黄人的向量。

谁能想出更好的结构或有效的解决方案?

正如 Jarod42 在对原始问题的评论中提到的,您可以使用访问者模式

这允许您在没有任何强制转换的情况下获得Card对象的实际类型。 它也被称为双重调度。

然后,这允许您将特定于某种类型的卡片的功能仅放在该卡片的类型中。 即不需要在Card中放置一个默认的虚拟GetHealth ()函数来引发异常。 只需在Minions卡片类型中声明一个GetHealth()函数, Spell将没有GetHealth()

这是一个小例子:

// Forward declare visitors so Accept function can be delared in Card.
struct CardVisitor;
struct ConstCardVisitor;

struct Card {
  virtual ~Card () = default;

  virtual void Activate () = 0;

  virtual void Accept (CardVisitor & obj) = 0;
  virtual void Accept (ConstCardVisitor & obj) const = 0;
};

// Forward declare derived Card types, so Visit function can be declared in visitors.
struct Minion;
struct Spell;

struct CardVisitor {
  virtual void Visit (Minion & obj) = 0;
  virtual void Visit (Spell & obj) = 0;
};

struct CardVisitor {
  virtual void Visit (Minion const & obj) = 0;
  virtual void Visit (Spell const & obj) = 0;
};

struct Minion : Card {
  int GetHealth () const;

  virtual void Activate () override;

  // Copy-paste exactly this code in every "final" type deriving from Card.
  virtual void Accept (CardVisitor & obj) override { obj.Visit(*this); }
  virtual void Accept (ConstCardVisitor & obj) const override { obj.Visit(*this); }
}

struct Spell : Card {
  int GetSmokeColor () const;
  bool DoesInstaKill () const;

  virtual void Activate () override;

  // Copy-paste exactly this code in every "final" type deriving from Card.
  virtual void Accept (CardVisitor & obj) override { obj.Visit(*this); }
  virtual void Accept (ConstCardVisitor & obj) const override { obj.Visit(*this); }
}

然后创建一个执行任何需要的访问者。 例如,让我们打印一些有关卡片的信息:

#include <iostream>
#include <memory>

struct PrintVisitor : ConstCardVisitor {
  virtual void Visit (Minion const & obj) override {
    std::cout << "health: " << obj.GetHealth();
  }

  virtual void Visit (Spell const & obj) override {
    std::cout << "smoke color: " << obj.GetSmokeColor()
      << ", does insta kill: " << obj.DoesInstaKill();
  }
}

int main () {
  // Base class pointers, i.e. to Card.
  std::unique_ptr<Card> cardA = std::make_unique<Minion>();
  std::unique_ptr<Card> cardB = std::make_unique<Spell>();

  PrintVisitor printVisitor;
  cardA->accept(printVisitor);
  cardB->accept(printVisitor);
};

PS: Card的析构函数应该是virtual。 它不在您的示例代码中。 在这种情况下,当您在Card指针上调用 delete 时(或者当std::unique_ptr为您执行它时),您的派生对象将不会被正确删除。

编辑:添加了Spell类以使访问者模式的优势更加清晰。

我会这样做:

class Card
{
public:
    virtual ~Card() = default;

    template <typename T>
    T* As() { return dynamic_cast<T*>(this); }
};

class Minion : public Card
{
public:
    int GetHealth() const {return mHealth;}

private:
    int mHealth = 100;
};

void test()
{
    std::unique_ptr<Card> card = std::make_unique<Minion>();
    if (auto* minion = card->As<Minion>())
        minion->GetHealth();
}

if (auto* minion = ...部分是检查类型是否为Minion并获取指向它的指针的紧凑方法,以便您可以调用基类中不存在的方法。

您要么需要将GetHealth放在Card ,要么稍后使用强制转换。 没有其它的方法。 由于强制转换是GetHealth ,这意味着Card需要GetHealth

为了避免强制转换,您可以将卡片类型作为数据成员存储在Card并使用非虚拟type()函数。 然后,各种卡片类型类的构造函数将使用适当的类型构造它们的Card部分。

为了防止像CardMinionMinion类的独立构造并只允许创建实际卡(如GoblinArmor等),请保护类型指定的构造函数:

class Card
{
public:
    enum class Type { Minion, Spell, Equipment };

    virtual ~Card() = default;
    Type type() const { return type_; }
    virtual int GetHealth() const { return 0; }

protected:
    explicit Card(const Type type) { type_ = type; }

private:
    Type type_;
};

class Minion : public Card
{
public:
    int GetHealth() const override { return mHealth; }

protected:
    Minion() : Card(Type::Minion) { }

private:
    int mHealth = 100;
};

class Goblin : public Minion
{ };

class Equipment : public Card
{
protected:
    Equipment() : Card(Type::Equipment) { }
};

class Armor : public Equipment
{ };

void test(Card& card)
{
    if (card.type() == Card::Type::Minion) {
        std::cout << "Health: " << card.GetHealth() << '\n';
    } else {
        std::cout << "Not a minion.\n";
    }
}

int main()
{
    Goblin goblin;
    Armor armor;
    test(goblin);
    test(armor);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM