TurnsCoffeeIntoScripts
TurnsCoffeeIntoScripts

Reputation: 3918

Casting pointer from base type to child type

I'm building a simple game design for a project of mine. I have the following classes:

class Character
{
public: 
   virtual void Display();
   virtual void SetParameters( char* param, ... );
};

class NonPlayableCharacter : public Character
{

public:
   virtual void Display();
   virtual void SetParameters( char* paaram, ... );
   int GetNPCState();
}

And then I have a bunch of classes that derive either from Character or NonPlayableCharacter. I define it like so:

std::vector<Character*> _allChar;

My problem is that at any given time I would want to perform some operation on one of the element of the vector. So getting an element out of the vector I can't directly call the method GetNPCState() because the element in the vector are of Character* type. So doing this:

_allChar[0]->GetNPCState();

doesn't work. So I tried doing it with the famous dynamic_cast:

NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]);
test->GetNPCState();

The problem with this last attempt is that GetNPCState() crashes because the object is null, and I know for a fact (via debugging) that _allChar[0] isn't null.

Upvotes: 8

Views: 13404

Answers (7)

Matthieu M.
Matthieu M.

Reputation: 299810

There are several types of casts in C++ (4), of which 2 are of interests here:

  • static_cast assumes that you know what you are doing
  • dynamic_cast checks, at runtime, that you "guessed" right

Note: simplified, as dynamic_cast also allows cross-casts and casts involving virtual bases.

There are 3 versions of dynamic_cast, really, depending on the nature of the target:

  • if the target is a reference (ie dynamic_cast<T&>(u)), then if the check fails dynamic_cast throws a std::bad_cast exception
  • if the target is a pointer (ie dynamic_cast<T*>(p)), then if the checks fails dynamic_cast returns a null pointer
  • finally, as a special case, if the target is void*, then dynamic_cast instead returns the address of the full object

In this case, you may:

  • switch from dynamic_cast<NonPlayableCharacter*>(_allChar[0])->getNPCState() to dynamic_cast<NonPlayableCharacter&>(*_allChar[0]).getNPCState(), and let the exception propagates
  • test the result of the cast (NonPlayableCharacter* test here) for non-nullity

Upvotes: 6

alestanis
alestanis

Reputation: 21863

To get a child pointer from a base pointer you do have to use dynamic_cast. Its behaviour is the following:

  • If the pointer points to a Child* allocated with new Child then dynamic_cast<Child*> returns a Child*
  • If the pointer points to something else, then dynamic_cast returns NULL.

Your problem is that either you didn't allocate with new, or your object is of a different type.

Upvotes: 2

CashCow
CashCow

Reputation: 31435

There is probably a better OO solution to using dynamic_cast, but the whole point of using this cast is that it will return a NULL pointer if the cast fails.

So check for NULL before you call GetNPCState();

NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]);
if( test != NULL )
{   
     test->GetNPCState(); 
}

Upvotes: 2

Aasmund Eldhuset
Aasmund Eldhuset

Reputation: 37950

dynamic_cast returns NULL when its argument doesn't point to a NonPlayableCharacter (so the first element in the array probably points to some other subclass of Character) - so you need to check for NULL after the cast. However, using dynamic_cast might be indicative of a design problem. Perhaps you should instead have a virtual method on Character that is called e.g. PerformMainActionInGameLoop() and is overridden appropriately in the different subclasses?

Upvotes: 2

juanchopanza
juanchopanza

Reputation: 227400

You have to test the dynamic_cast for success. It returns a null pointer upon failure:

NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]);
if (test) test->GetNCPState();

The problem could be that your first element does not point to a NonPlayableCharacter object.

Upvotes: 2

Luchian Grigore
Luchian Grigore

Reputation: 258598

After

NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]);

you should check if test is NULL. Even if _allChar[0] isn't NULL, the dynamic_cast can return NULL if the object it points to isn't a NonPlayableCharacter.

So the correct version would be:

NonPlayableCharacter* test = dynamic_cast<NonPlayableCharacter*>(_allChar[0]);
if (test)
{
   test->GetNPCState();
}

Upvotes: 5

Denis Ermolin
Denis Ermolin

Reputation: 5546

dynamic_cast returns NULL if cast is impossible. Check what is inside _allChar[0]. You could make function like getType() where returns object predefined type id and then use static_cast:

if (_allChar[0]->getType() == TYPE_NO_PLAYER) {
    static_cast<NonPlayableCharacter*>(_allChar[0])->getNpcState();
}

Upvotes: 3

Related Questions