-1

I am working on a game that has monsters and dragons. Dragons can do everything monsters can do except they can also breathe fire.

I have made a class of type 'monster' and a class that inherits from monster called 'dragon'.

I then have a class called 'monsters' which has as a private member vector which will contain dragon and monster elements.

In the main game loop I need to cycle through the vector and blow fire if the current element is a dragon, and do nothing if it is just a monster.

I have tried using typeid() but it always returns monster* whether the current element is a plain monster or a dragon.

Is there any way to do this or does it not make sense to even use inheritance in this case? Would it make more sense for the dragon class to not inherit, and to instead stand by itself independent of monster?

Any advice is appreciated.

  • 2
    Is it possible that what your code *actually* wants to do is to `attack`? "breathe fire" sounds like a particular form of attack, and I suspect other Monsters can attack as well. So maybe just have a `virtual void Monster::attack()` method and reimplement that to cast fire for dragons. – Frerich Raabe Sep 05 '12 at 12:15
  • -1: Looks for me like a "do it for me" question. Is it a homework-task of a C++ course? – Valentin H Sep 14 '12 at 10:17

5 Answers5

3

You could do something like this.

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.
    }
}

But it sounds like you should arrange your classes a little better. Why don't you just have all your monster classes have an Attack method? This way you can override the methods for each different monster type.

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!
Aesthete
  • 18,622
  • 6
  • 36
  • 45
  • Hi. Thanks very much for the response. I think you are right and having a virtual attack function makes much more sense. – ajohndaniels Sep 05 '12 at 15:16
1

Not that this is the best way to do it, but just for your information, you were calling typeid on the pointer which doesn't work if you want the runtime type of the object. typeid does work at runtime when it needs to, but you need to dereference pointers to polymorphic types:

typeid(*monsterpointer)

And that will get the runtime type of *monsterpointer, be it a monster or dragon.

However, typeid is only for exact comparisons, so if you add another class called ImpairedDragon that inherits from Dragon, the typeid code will break and not work with them. So you should probably use dynamic_cast which works with inheritance, or even better, a virtual function if you can think of how to use one well.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • Wow that was dumb of me... Thanks for the advice. From the other posts I see why this isn't the best method but its still good to know how to do it. – ajohndaniels Sep 05 '12 at 15:22
1

There are two possible approaches: use a virtual function or use a dynamic_cast. If monster has a virtual function blow_fire() that does nothing, dragon can override it to do whatever is appropriate for a dragon. Then you just call it for all the monster objects, and the dragons will blow fire. Using dynamic_cast, the code would be something like this:

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

That's generally viewed as a bad approach, because it doesn't grow naturally. If you added another type of monster that could blow fire you'd have to add another dynamic_cast. With a virtual function, the new type would just override the virtual function.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
1

If you've got monster pointers in your container, then public monster members are all you are able to access through those pointers in a type-safe way (from my recollection). You might try resorting to an interface-based design á la COM so that you can query the monster base class for any extra features i.e. interfaces. I'd provide you with a sample but my lunchtime is now over.

-2

How about putting an empty (no operation) virtual blowFire in the base class, and then overriding it with something in the derived class?

It would be even better to rename the method to specialAttack or a similar generic name, so you don't need to add similar methods for other kinds of Monsters.

piokuc
  • 25,594
  • 11
  • 72
  • 102
  • @LuchianGrigore - shrug. Sometimes Object Oriented Purity has to give way to practicality. – Pete Becker Sep 05 '12 at 12:14
  • @PeteBecker no, just don't use OO in this case if you can't think of how to do it right. – Seth Carnegie Sep 05 '12 at 12:15
  • Then Dragon shouldn't be derived from Monster, or they shouldn't be kept together in a container. Using dynamic_cast in this case is very bad design. – piokuc Sep 05 '12 at 12:16
  • @piokuc A Dragon is a Monster. Care to back your statement up? – Luchian Grigore Sep 05 '12 at 12:17
  • Gosh, lots of people saying not to go object-oriented, without recommending **specific** and **practical** solutions. Programming is about making things work; if abstract principles make that harder, it's time to question the principles. – Pete Becker Sep 05 '12 at 12:18
  • @Luchian Grigore In this case I insist it is better to have `blowFire` in Monster which does nothing, perhaps along with `bool canBlowFire() { return false; }` and override it in Dragon, rather then use `dynamic_cast`. – piokuc Sep 05 '12 at 12:20
  • 1
    @Luchian Grigore I don't say it's the best, but it's better than `dynamic_cast` – piokuc Sep 05 '12 at 12:26
  • @Luchian Grigore Did you just remove your answer suggesting dynamic_cast? Why? – piokuc Sep 05 '12 at 12:30
  • @piokuc because I failed to see there's a class called `monsters` which holds a collection of monsters (thought the collection was inside the class itself). **Not** because of the `dynamic_cast`. – Luchian Grigore Sep 05 '12 at 12:31