C++ 使用纯多态性和继承性在基类的派生类上调用函数而无需强制转换?
我的问题是: 我有一个纸牌游戏,它由仆从、法术和装备组成,这些都是纸牌的类型,我知道虚拟定义的方法。我需要在C++ 使用纯多态性和继承性在基类的派生类上调用函数而无需强制转换?,c++,inheritance,polymorphism,C++,Inheritance,Polymorphism,我的问题是: 我有一个纸牌游戏,它由仆从、法术和装备组成,这些都是纸牌的类型,我知道虚拟定义的方法。我需要在仆从上使用GetHealth()、RemoveHealth()和AddHealth()函数,但我不希望在拼写或设备上使用这些函数。我想做的是: class CCard { //Member variables and functions }; class CMinion : public CCard { //Member variables and functions i
仆从
上使用GetHealth()
、RemoveHealth()
和AddHealth()
函数,但我不希望在拼写
或设备
上使用这些函数。我想做的是:
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.
类CCard
{
//成员变量和函数
};
类别名称:公共CCard
{
//成员变量和函数
int GetHealth()常量{return mHealth;}
};
std::unique\u ptr minion=std::make_unique(参数);
仆从->获取健康()//我意识到这是不可能的。
我现在有一个单独的名单,上面有一张牌的仆从和ID,当我手里拿着这张牌时,我把仆从对象放在桌子上而不是牌上。我的讲师想要纯多态性和继承,所以这是一个笨拙的解决方案。
我还讨论了将GetHealth()
和其他必需的方法放入Card类中,并使不需要它的类返回0。他说:“这是一个更好的解决方案,但我还是应该使用继承和多态性。”
他侦察到我的架构是错误的,我应该重组它
我应该:
作为仆从
、法术
或装备
实例的卡片向量。桌子、手牌和牌组都应该是牌的矢量,但我目前的解决方案是牌组和手牌是牌的矢量,我的桌子是仆从的矢量
有谁能想出更好的结构或有效的解决方案吗?我会这样做:
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();
}
类卡
{
公众:
virtual~Card()=默认值;
模板
T*As(){return dynamic_cast(this);}
};
职业仆从:公共卡
{
公众:
int GetHealth()常量{return mHealth;}
私人:
int mHealth=100;
};
无效测试()
{
std::unique_ptr card=std::make_unique();
if(auto*minion=card->As())
仆从->获取健康();
}
if(auto*minion=…
part是一种紧凑的方法,用于检查类型是否为minion
,并获取指向它的指针,以便您可以调用基类中不存在的方法。您需要将GetHealth
放入卡中,或者稍后使用强制转换。没有其他方法。因为强制转换是不可能的,这意味着e> 卡片
需要GetHealth
为了避免强制转换,您可以将卡类型存储为card
中的数据成员,并使用非虚拟的type()
函数。然后,各种卡类型类的构造函数将使用适当的类型构造其卡
部分
为了防止基本卡类(如card
,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);
}
类卡
{
公众:
枚举类类型{仆从、法术、装备};
virtual~Card()=默认值;
Type Type()常量{返回类型}
虚拟int GetHealth()常量{return 0;}
受保护的:
显式卡(常量类型){Type\uux=Type;}
私人:
类型\类型;
};
职业仆从:公共卡
{
公众:
int GetHealth()常量重写{return mHealth;}
受保护的:
Minion():卡(类型::Minion){}
私人:
int mHealth=100;
};
地精类:公共仆从
{ };
类别设备:公共卡
{
受保护的:
设备():卡(类型::设备){}
};
甲级:公共装备
{ };
无效测试(卡片和卡片)
{
if(card.type()==card::type::Minion){
正如Jarod42在对原始问题的评论中提到的,您可以使用
这允许您在不进行任何强制转换的情况下获取卡
对象的实际类型。这也称为双重分派
这样,您就可以将特定于某一类型卡的函数仅放在该卡的类型中。也就是说,无需将引发异常的默认虚拟GetHealth()
函数放在card
中。只需声明一个GetHealth()
功能在仆从
卡类型中,拼写
将不会有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);
};
#包括
#包括
结构PrintVisitor:ConstCardVisitor{
虚拟无效访问(仆从const&obj)覆盖{
std::无法使GetHealth
成为CCard
@acraig5075:GetHealth
的纯虚拟成员函数,这对Spell
/设备
没有意义……注意:手动内存管理并不“简单”无论如何,实际的代码是可以的。没有一种操作对一种以上的类型有意义,但对每种类型都必须有不同的实现=>没有多态性的情况,这是讲师的愿望。你怎么知道你攻击的是仆从,而不是拼写或设备?你需要(或者仅仅需要)?标题中表示不强制转换,这包括所有强制转换,无论是动态强制转换、静态强制转换还是任何其他强制转换。讲师说他不希望这样。@XenoXetric您可能误解了作业中的某些内容。不强制转换就不可能通过基类引用或指针调用仅存在于子类中的成员函数@NikosC。使用访问者模式是完全可能的(参见Jarod42对原始问题的评论)。不需要强制转换。这对GetHealth()、RemoveHealth()和AddHealth()三种方法都有效-我需要添加三种“接受”每一个派生类的最小值都是可以的。但是我的法术类也会有这个值,即使我可以在法术中把它保留为空(因为sp)