我需要制作一个包含基类和派生类对象的向量,并知道哪个元素是哪个

Posted

技术标签:

【中文标题】我需要制作一个包含基类和派生类对象的向量,并知道哪个元素是哪个【英文标题】:I need to make a vector containing objects of a base class and a derived class and know which element is which 【发布时间】:2012-09-05 12:06:31 【问题描述】:

我正在开发一款包含怪物和龙的游戏。龙可以做怪物能做的所有事情,除了它们也会喷火。

我创建了一个类型为“monster”的类和一个继承自名为“dragon”的怪物的类。

然后我有一个名为“monsters”的类,它作为私有成员向量,将包含龙和怪物元素。

在主游戏循环中,如果当前元素是龙,我需要循环遍历矢量并喷火,如果它只是怪物,则什么也不做。

我尝试过使用 typeid(),但无论当前元素是普通怪物还是龙,它总是返回 monster*。

有没有办法做到这一点,或者在这种情况下甚至使用继承也没有意义? 龙类不继承,而是独立于怪物独立存在是否更有意义?

感谢任何建议。

【问题讨论】:

你的代码实际上想要做的是attack吗? “喷火”听起来像是一种特殊的攻击形式,我怀疑其他怪物也可以攻击。所以也许只是有一个virtual void Monster::attack() 方法并重新实现它来为龙施放火。 -1:对我来说,这就像一个“为我做”的问题。这是 C++ 课程的家庭作业吗? 【参考方案1】:

可以做这样的事情。

class Monster ;
class Dragon : public Monster

  public:
    void BlowFire() 


std::vector<Monster*> monsters; // Collection of monsters

for(auto it = monsters.begin(); it != monsters.end(); ++it)

    Dragon* pDragon = dynamic_cast<Dragon*>(*it); // Attempt to convert pointer
    if(pDragon)
    
       pDragon->BlowFire(); // If it is indeed a Dragon, blow fire.
    

但听起来你应该更好地安排你的课程。你为什么不让你所有的怪物类都有一个Attack 方法?这样您就可以覆盖每种不同怪物类型的方法。

class Monster 

  public:
    virtual void Attack()  // All monsters can attack
;

class Dragon : public Monster

  public:
    virtual void Attack()  // Do something special for a Dragon.       
;

Monster myMonster;
Dragon myDragon;
myMonster.Attack(); // Generic monster attack.
myDragon.Attack(); // Special super dragon fire attack!

【讨论】:

嗨。非常感谢您的回复。我认为你是对的,拥有虚拟攻击功能更有意义。【参考方案2】:

有两种可能的方法:使用虚函数或使用dynamic_cast。如果monster 有一个虚函数blow_fire() 什么都不做,dragon 可以覆盖它来做任何适合龙的事情。然后你只需为所有monster 对象调用它,龙就会喷火。使用dynamic_cast,代码会是这样的:

if (dragon *dragon_ptr = dynamic_cast<dragon*>(monster_ptr)) 
    dragon_ptr->blow_fire();

这通常被视为一种不好的方法,因为它不会自然生长。如果您添加了另一种可以喷火的怪物,则必须添加另一个dynamic_cast。有了虚函数,新类型只会覆盖虚函数。

【讨论】:

【参考方案3】:

并不是说这是最好的方法,只是为了您的信息,您在 pointer 上调用 typeid 如果您想要对象的运行时类型,这将不起作用. typeid 确实在需要时在运行时工作,但您需要取消引用指向多态类型的指针:

typeid(*monsterpointer)

这将获得*monsterpointer 的运行时类型,无论是monster 还是dragon

但是,typeid 仅用于精确比较,因此如果添加另一个继承自 Dragon 的名为 ImpairedDragon 的类,typeid 代码将中断并且无法与它们一起使用。所以你可能应该使用dynamic_cast,它可以与继承一起使用,如果你能想到如何很好地使用一个虚函数,甚至更好。

【讨论】:

哇,我真傻……谢谢你的建议。从其他帖子中我明白了为什么这不是最好的方法,但知道如何去做仍然很好。【参考方案4】:

如果你的容器中有怪物指针,那么公共怪物成员就是你能够以类型安全的方式通过这些指针访问的所有成员(据我的回忆)。您可能会尝试采用基于接口的设计 á la COM,以便您可以查询怪物基类的任何额外功能,即接口。我会为您提供样品,但我的午餐时间已经结束了。

【讨论】:

【参考方案5】:

如何在基类中放置一个空(无操作)虚拟blowFire,然后用派生类中的某些内容覆盖它?

最好将方法重命名为specialAttack或类似的通用名称,这样就不需要为其他种类的怪物添加类似的方法了。

【讨论】:

@LuchianGrigore - 耸耸肩。有时,面向对象的纯度必须让位于实用性。 @PeteBecker 不,如果您想不出正确的方法,就不要在这种情况下使用 OO。 那么 Dragon 不应该从 Monster 派生,或者它们不应该放在一个容器中。在这种情况下使用 dynamic_cast 是非常糟糕的设计。 @piokuc 龙就是怪物。愿意支持您的陈述吗? @Luchian Grigore 我不说它是最好的,但它比dynamic_cast更好

以上是关于我需要制作一个包含基类和派生类对象的向量,并知道哪个元素是哪个的主要内容,如果未能解决你的问题,请参考以下文章

在基类类型的向量中保存的基对象和派生对象被切片

关于C++基类与派生类

C#编程,关于基类和派生类

派生类和基类的转换

最佳实践:将派生类的向量传递给基类向量上的方法

基类和派生类