RTTI — Asking a Type at Run Time
Rocket Run keeps every enemy in one list of Enemy* pointers: drones, asteroids and the occasional boss. When the player lands a hit, the boss must move to its next phase, but a drone has no phases. The code holds a plain Enemy*. How can it find out what the object really is? That is what run-time type information, or RTTI, is for.
The rule
RTTI lets a base pointer ask which derived type it really points to.
A derived class is a more specific kind of its base class:
struct Enemy {
virtual ~Enemy() = default; // virtual: Enemy is now polymorphic
int health = 10;
};
struct Boss : Enemy { int phase = 1; };
struct Drone : Enemy {};RTTI works only for polymorphic classes: those with at least one virtual function. The virtual destructor above is enough. With that in place you have two tools:
dynamic_cast converts to the derived type if the object really is one:
void onHit(Enemy* enemy) {
if (auto* boss = dynamic_cast<Boss*>(enemy)) {
++boss->phase; // only for bosses
}
}With a pointer, a failed cast returns nullptr. With a reference, dynamic_cast<Boss&>(e), a failure throws std::bad_cast, since there is no null reference.
typeid (header <typeinfo>) compares exact types: typeid(*enemy) == typeid(Boss). Its .name() text is implementation-defined; GCC, for instance, prints a mangled name, so use it only for debugging.
The shortcut
Before reaching for RTTI, ask: "Could the object answer this itself?" If every enemy should react to a hit in its own way, give Enemy a virtual void onHit() and let each class override it. Use dynamic_cast for the rare case where one piece of code truly needs the concrete type.
What the code holds
Enemy* enemypoints at a Drone object.
The trap is forgetting virtual. Without it, typeid(*enemy) reports the static type Enemy, and dynamic_cast from Enemy* down to Boss* will not even compile.
Drills
Answer fast — trust the rule.
enemy points at a Drone. What does dynamic_cast<Boss*>(enemy) return?
What must Enemy have for RTTI to see an object's real type?
enemy points at a Boss and Enemy is polymorphic. What is typeid(*enemy) == typeid(Boss)?
What happens when dynamic_cast<Boss&>(someEnemy) fails?
Is the text from typeid(x).name() the same on every compiler?
Remember
- RTTI lets a base pointer ask which derived type it really points to.
- Shortcut: first ask whether a virtual function could answer instead.
- Trap: no
virtual, no RTTI; a failed pointer cast isnullptr, a failed reference cast throws.
