Reputation: 730
I am having several problems trying to downcast a class into another one to access an specific method of that class. This is my current scheme of classes:
GameObject class:
class GameObject
{
...
}
Enemy class:
#include "../GameObject.h"
class Enemy : public GameObject
{
Enemy(Type type);
virtual ~Enemy();
virtual int receiveDamage(int attack_points);
virtual void levelUp() = 0;
...
protected:
char *name_;
int level_;
int health_;
int max_health_;
int attack_;
int armor_;
}
SmallGoblin class:
#include "../Enemy.h"
class SmallGoblin : public Enemy
{
public:
SmallGoblin();
~SmallGoblin();
void levelUp();
}
In my code, I try to do this and a std::bad_cast exception is thrown every time.
class Player : GameObject
{
...
virtual void attack(GameObject &enemy)
{
try
{
Enemy &e = dynamic_cast<Enemy&>(enemy);
e.receiveDamage(attack_points_);
}
catch(const std::bad_cast& e)
{
std::cerr << e.what() << '\n';
std::cerr << "This object is not of type Enemy\n";
}
}
...
}
(enemy is a reference to a GameObject object, however I know it's actually a SmallGoblin object).
In other part my code I have anoother class (Door) which extends the GameObject class and the downcasting works (however, I have to use static_cast instead dynamic_cast I don't know why).
Upvotes: 0
Views: 349
Reputation: 7271
You mention in one of your comments that you are storing std::vector<GameObject>
. Unfortunately, this will cause your GameObject
objects to be sliced. A great description can be found here: What is object slicing?
"Slicing" is where you assign an object of a derived class to an instance of a base class, thereby losing part of the information - some of it is "sliced" away.
To solve this problem, you need to store a vector of pointers. You have some choices here, if you're using C++11. You can store either:
std::vector<GameObject*> badGameObjects;
std::vector<std::unique_ptr<GameObject>> uGameObjects;
std::vector<std::shared_ptr<GameObject>> sGameObjects;
All of these options will ensure that slicing doesn't happen because the vector is just storing pointers. Storing naked pointers is the least desirable option because you will have to manage the memory yourself and can be a source of memory leaks. The use of unique_ptr
or shared_ptr
will depend on how you need to use the objects.
Upvotes: 4
Reputation: 464
dynamic_cast
shouldn't be used for downcasting, I'm pretty sure static_cast
does the job. dynamic_cast
is used for "upcasting" instead.
Upvotes: -1
Reputation: 596352
If dynamic_cast
is failing, then enemy
is not actually a valid Enemy
instance, so double-check how you are managing that reference.
The following works fine for me when I try it:
class GameObject
{
public:
virtual ~GameObject(){}
};
enum Type {goblin};
class Enemy : public GameObject
{
public:
Enemy(Type type) : type_(type) {}
virtual ~Enemy() {}
virtual int receiveDamage(int attack_points) {}
virtual void levelUp() = 0;
protected:
Type type_;
//...
};
class SmallGoblin : public Enemy
{
public:
SmallGoblin() : Enemy(goblin) {}
~SmallGoblin() {}
void levelUp() {}
};
class Player : GameObject
{
public:
int attack_points_;
virtual void attack(GameObject &enemy)
{
try
{
Enemy &e = dynamic_cast<Enemy&>(enemy);
e.receiveDamage(attack_points_);
}
catch(const std::bad_cast& e)
{
std::cerr << e.what() << '\n';
std::cerr << "This object is not of type Enemy\n";
}
}
};
.
Player p;
SmallGoblin goblin;
p.attack(goblin);
BTW, I would use dynamic_cast
with pointers instead, to avoid the unnecessary overhead of using exceptions:
virtual void attack(GameObject &enemy)
{
Enemy *e = dynamic_cast<Enemy*>(&enemy);
if (e)
e->receiveDamage(attack_points_);
else
std::cerr << "This object is not of type Enemy\n";
}
Upvotes: 2