Reputation: 3918
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
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 doingdynamic_cast
checks, at runtime, that you "guessed" rightNote: 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:
dynamic_cast<T&>(u)
), then if the check fails dynamic_cast
throws a std::bad_cast
exceptiondynamic_cast<T*>(p)
), then if the checks fails dynamic_cast
returns a null pointervoid*
, then dynamic_cast
instead returns the address of the full objectIn this case, you may:
dynamic_cast<NonPlayableCharacter*>(_allChar[0])->getNPCState()
to
dynamic_cast<NonPlayableCharacter&>(*_allChar[0]).getNPCState()
, and let the exception propagatesNonPlayableCharacter* test
here) for non-nullityUpvotes: 6
Reputation: 21863
To get a child pointer from a base pointer you do have to use dynamic_cast
. Its behaviour is the following:
Child*
allocated with new Child
then dynamic_cast<Child*>
returns a Child*
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
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
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
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
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
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