簡體   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