![](/img/trans.png)
[英]Call derived class method from base class instance without casting
[英]Using purely polymorphism and inheritence to call functions on a derived class from base class without casting?
我的問題是:我有一個紙牌游戲,它由小兵、法術和裝備組成,這些都是卡的類型,我知道方法的虛擬定義。 我需要使用GetHealth()
RemoveHealth()
和AddHealth()
在功能Minion
,但我不希望這些在Spell
或Equipment
。 我希望做的是:
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。他說“這是一個更好的解決方案,但我還是應該使用繼承和多態性”。
他認為我的架構是錯誤的,我應該對其進行重組。
我應該有: 卡片向量是Minion
、 Spell
或Equipment
的實例。 桌子、手和牌應該都是卡片的向量,但我目前的解決方案是牌和手是卡片的向量,而我的桌子是小黃人的向量。
誰能想出更好的結構或有效的解決方案?
正如 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
部分。
為了防止像Card
、 Minion
等Minion
類的獨立構造並只允許創建實際卡(如Goblin
、 Armor
等),請保護類型指定的構造函數:
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.