2015-11-15 39 views
2

我希望这个问题还没有在其他地方得到解答,我确实搜索了一个答案,但是不能很好地规范搜索。C++当派生类没有从接口实现函数时该怎么办

问题是,我有一个武器抽象类武器需要的统计数据(如准确性,损坏等)。这是一个抽象类,因为有些武器对重装和破坏有不同的实现方式,但其中大多数武器没有。

Weapon类具有DoT(结构体)成员以及getDoT成员函数。

问题是,并非所有的武器都有DoT。但是在代码中的某个时刻,在处理攻击时,Weapon :: getDoT被调用。当然,我之前添加了“has_DoT()”方法,所以我不处理单元化的DoT对象,但这是一个相当糟糕的解决方案。

处理这个问题的最佳方法是什么?当并非所有派生对象实际上都拥有抽象类中的成员?

我以为首先没有DoT成员/ getDoT成员函数,只是在派生类Weapon_With_DoT上实现它们,但由于我的抽象类是Weapon,我仍然需要检查是否武器有一个DoT,然后从武器类型转换为Weapon_With_DoT来访问Weapon_With_DoT :: getDoT成员函数。这也是不合理的,只是相反。

我希望问题能够以足够清晰的方式解释。 :)

+0

如果不是所有的武器都有DoT,那么它可能不应该在顶级抽象类中。是否有武器的子类具有该属性?我会考虑为什么一些武器有这个属性,有些不是从设计的角度来看。 – Joe

+0

乔是绝对正确的。应该使用顶级抽象类来定义常见行为。这种差异应该定义在较低的水平。 – Greg

+0

事后看来,只有在界面中定义了常见行为才有意义,但如果代码的其余部分使用武器作为界面,您将如何处理该问题? – deso

回答

0

DoT代表损伤时间,对吧?所以我认为这是一个随着时间的推移受到伤害统计的物体,它具有损伤量和时间量的属性。

选项1:空物体

让武器类具有getDoT方法。没有DoT的武器仍然可以为其所有字段返回零和/或中性值的DoT对象。使用DoT并知道这些字段是什么的代码可以使用if语句检测到这种情况,并执行正确的操作(例如,如果损坏量为零,则在发生任何影响之前返回)。

这将是Null Object pattern的一个示例。

选项2:判断方法

让武器类有hasDoT方法返回一个布尔值,并返回了有点的getDoT方法。你说这是你的代码目前的样子。我知道这不是一个受欢迎的解决方案,但它很容易理解,并且可以完成工作,所以您不应该为此感到尴尬。

我可能会添加一个断言或抛出一个异常来捕获在没有DoT的武器上调用getDoT的情况。

选项3:抽象类

getDoT方法中的抽象类。这个类可以是它自己的东西,也可以是Weapon的子类,具体取决于您的应用程序的意义。您可以使用某种形式的动态转换将Weapon指针转换为正确类型的指针。其他答案有关于如何做到这一点的更多细节。

这实际上是最不灵活的选择,因为它假定我们知道在编译时哪些武器具有DoT效应。在你的游戏的下一个版本中,你可能想要有一种在某个时间点获得DoT的武器(例如,当用户切换到不同的模式,或者当它没有被激发10秒)。如果你作出这样的决定,你可能必须改变所有代码的使用选项1或选项2

约绝对的点

我看其他人谁支持选项3说这样的话“绝对正确”和“故事结束”。你不应该相信他们!设计模式不是规则。有很多方法可以编写出好的代码。真正重要的是,您的代码易于阅读,易于编写,易于在将来进行更改,不会造成不必要的危险,并且完成预期的工作。

1

不是所有的武器具有点

然后基类的所有武器不应该提供getDoT。故事结局。

但因为我的抽象类是武器,我仍然需要进行检查,看是否该武器有一个点,然后从武器类型强制转换为Weapon_With_DoT访问Weapon_With_DoT :: getDoT

这种方式就是疯狂。

,而不是代码,看起来像这样

if (I can haz DoT) { 
    DoT = gets DoT from weapon somehow // null pointer or optional or dynamic_cast 
    doSomethingImportant(DoT) 
} 

try代码看起来像这样

weapon->doSomethingImportant(); 

其中doSomethingImportant是一个虚函数。它在DoT上做了一些事情,或者什么也不做,或者做一些DoT以外的事情。 你不在乎它在做什么,你知道这种武器是正确的。

如果由于某种原因无法将doSomethingImportant添加到Weapon(并且可能存在合法原因),请尝试使用Visitor做同样的事情。

+0

一般来说,代码中的任何(是,*任何*,这不是玩笑)'if语句是可疑的,可能应该像上面显示的那样替换。例外情况应该很少。 –

+0

如果目标是消除if,那么我会考虑任何形式的消息传递,比如:'weapon-> process(Event(“reload”));' –

+0

@ c-smile这不是一个目标,它意味着达到目的(清洁可维护的代码)。但是,消息系统也可能有用。 –

1

在像你这样的情况下,考虑任何种类的运行时多态性都是有意义的。如动态铸造或这样的事情:

class Weapon { 
    enum { CLASS_ID: 1 }; 

    public: 
    template<typename T> 
    T* get_interface() { 
     return static_cast<T*>(this->get_interface_by_id(T::CLASS_ID)); 
    } 
    virtual Weapon* get_interface_by_id(int class_id) { 
     if(class_id == CLASS_ID) return this; 
     return nullptr; // no other interfaces by default 
    } 
} 

class WeaponWithDot: public Weapon { 
    enum { CLASS_ID: 2 }; 

    virtual Weapon* get_interface_by_id(int class_id) override { 
     if(class_id == CLASS_ID) return this; 
     return Weapon::get_interface_by_id(class_id); 
    } 
} 

这样你仍然可以操作武器的情况,并要求动态他们的特点和功能:

Weapon* some = ...; 

WeaponWithDoT* with_dot = some->get_interface<WeaponWithDoT>(); 
if (with_dot) ... 
0

然后取出从Weapon类点数据,以便它仍然是干净的,并继承它作为DotWeapon。你仍然可以从DotWeapon得到来自对象的Weapon的多晶现象。

我想过没有在 首位电信部成员/ getDoT成员函数,并且只实现他们在派生类中 Weapon_With_DoT,但因为我的抽象类是武器,我仍然会 需要检查看武器是否有DoT,然后从Weapon到Weapon_With_DoT的类型转换 访问Weapon_With_DoT :: getDoT 成员函数。这也是不合理的,只是相反。

你完全没有掌握到多态性。 基类对衍生类应该一无所知。他们只知道他们所包含的内容,他们的派生类填补了空白。

相关问题