C++ RTTI
In this page:
#include <typeinfo>
typeid(expression).name();
typeid(expression1) == typeid(expression2);
What is RTTI?
RTTI, or Run-Time Type Information, is the mechanism C++ provides for determining the exact, actual type of an object while the program is running, rather than relying only on the static type known at compile time.
Example: What is RTTI?
// Include std::cout and std::cin
#include <iostream>
#include <typeinfo>
// Define the type Animal
class Animal {
// Members below can be accessed from outside the class
public:
virtual ~Animal() {}
};
// Define the type Dog
class Dog : public Animal {};
// Program execution starts in main()
int main() {
// Allocate an object on the heap with new
Animal *a = new Dog();
// Print to the console with cout
std::cout << typeid(*a).name() << std::endl;
// Free the heap memory with delete
delete a;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
The typeid Operator
The typeid operator returns an object representing the real runtime type of an expression, which you can compare against another typeid result or use to print a human-readable (if compiler-specific) name of the class involved.
Example: The typeid Operator
// Include std::cout and std::cin
#include <iostream>
#include <typeinfo>
// Program execution starts in main()
int main() {
// Declare x and set it to 5
int x = 5;
// Declare y and set it to 3.14
double y = 3.14;
// Print to the console with cout
std::cout << typeid(x).name() << " " << typeid(y).name() << std::endl;
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Dynamic Cast on Pointers
The dynamic_cast operator safely converts a base class pointer into a derived class pointer, checking at runtime whether the object actually is of that derived type — if the cast isn't valid, it returns nullptr instead of producing an invalid pointer.
Example: Dynamic Cast on Pointers
// Include std::cout and std::cin
#include <iostream>
// Define the type Animal
class Animal {
// Members below can be accessed from outside the class
public:
virtual ~Animal() {}
};
// Define the type Dog
class Dog : public Animal {
// Members below can be accessed from outside the class
public:
void bark() { std::cout << "Woof" << std::endl; }
};
// Program execution starts in main()
int main() {
// Allocate an object on the heap with new
Animal *a = new Dog();
Dog *d = dynamic_cast<Dog *>(a);
if (d != nullptr) d->bark();
// Free the heap memory with delete
delete a;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Dynamic Cast on References
dynamic_cast also works on references, but since a reference can never be null, an invalid cast on a reference throws a std::bad_cast exception instead of silently returning a null value, which you're expected to catch if the cast might fail.
Example: Dynamic Cast on References
// Include std::cout and std::cin
#include <iostream>
// Define the type Animal
class Animal {
// Members below can be accessed from outside the class
public:
virtual ~Animal() {}
};
// Define the type Dog
class Dog : public Animal {
// Members below can be accessed from outside the class
public:
void bark() { std::cout << "Woof" << std::endl; }
};
// Program execution starts in main()
int main() {
Dog realDog;
Animal &a = realDog;
// Try code that might throw an exception
try {
Dog &d = dynamic_cast<Dog &>(a);
d.bark();
// Handle the exception if one was thrown
} catch (std::bad_cast &e) {
std::cout << "Invalid cast" << std::endl;
}
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Best Practices with RTTI
RTTI checks carry a small but real runtime cost, so where possible it's preferable to design your class hierarchy around ordinary virtual function polymorphism, reserving RTTI for the specific cases — like certain plugin or serialization systems — where you genuinely need to inspect a type at runtime.
Example: Best Practices with RTTI
// Include std::cout and std::cin
#include <iostream>
// Define the type Animal
class Animal {
// Members below can be accessed from outside the class
public:
virtual void speak() { std::cout << "..." << std::endl; }
};
// Define the type Dog
class Dog : public Animal {
// Members below can be accessed from outside the class
public:
void speak() override { std::cout << "Woof" << std::endl; }
};
// Program execution starts in main()
int main() {
// Allocate an object on the heap with new
Animal *a = new Dog();
a->speak();
// Free the heap memory with delete
delete a;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
- Using
dynamic_caston a class with no virtual functions, which does not compile. - Not checking for a null result of
dynamic_caston pointers before using it. - Using
typeidon a pointer and expecting the type of the object rather than the pointer.
- Polymorphism lets the same call behave differently, using function overriding and virtual functions.
- Pure virtual functions and abstract classes define interfaces that derived classes must implement.
- Encapsulation, abstraction, object slicing, and RTTI are related concepts for designing class hierarchies.
Chapter Quiz — Complete all 11 topics to unlock
0/11 topics done
Complete these topics first: