[英]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.