Run-Time Type Information (RTTI)

Run-Time Type Information (RTTI) is a mechanism in C++ that allows the type of an object to be determined during runtime. While C++ is statically typed, meaning that type information is typically resolved at compile-time, RTTI enables certain scenarios where the type needs to be identified during program execution.

RTTI is primarily used in polymorphic situations, where a pointer or reference to a base class can point to objects of derived classes. It helps to discover the exact type of the object at runtime.

Key Concepts in RTTI

There are two primary components of RTTI in C++:

  1. typeid Operator
  2. dynamic_cast Operator

1. typeid Operator

The typeid operator allows you to query the type of an object at runtime. It returns a reference to a type_info object, which contains information about the type.

Syntax:

typeid(expression)

Where expression can be:

  • A variable or an object (for polymorphic objects, the typeid will return the type of the actual derived object).
  • A type (in this case, it provides the static type information at compile-time).

Example:

#include <iostream>
#include <typeinfo>

class Base {
public:
    virtual ~Base() {}
};

class Derived : public Base {};

int main() {
    Base* basePtr = new Derived();  // Pointer to a derived object

    std::cout << "Type of basePtr: " << typeid(*basePtr).name() << std::endl;
    return 0;
}

In the example, typeid(*basePtr) will return the actual type of the object (Derived), not the static type (Base), because basePtr points to a Derived object. Note that typeid works correctly with polymorphic types (i.e., classes with at least one virtual function).

2. dynamic_cast Operator

The dynamic_cast operator is used to safely cast a pointer or reference from one type to another in a class hierarchy, particularly for casting between base and derived classes.

It checks at runtime if the casting is valid:

  • It can safely cast a pointer or reference to a base class into a pointer or reference to a derived class.
  • If the cast is valid, it returns a pointer to the derived type.
  • If the cast is invalid (e.g., trying to cast a base class pointer to a derived class that it does not point to), it returns nullptr (for pointer casts) or throws a std::bad_cast exception (for reference casts).

Syntax:

dynamic_cast<new_type>(expression)

Where new_type is the type you want to cast to, and expression is the object being cast.

Example:

#include <iostream>

class Base {
public:
    virtual ~Base() {}
};

class Derived : public Base {
public:
    void derivedMethod() {
        std::cout << "Derived class method called" << std::endl;
    }
};

int main() {
    Base* basePtr = new Derived();  // Pointer to base class pointing to derived object

    Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
    if (derivedPtr) {
        derivedPtr->derivedMethod();  // Safe to call derived class method
    } else {
        std::cout << "Cast failed!" << std::endl;
    }

    return 0;
}

In this example, dynamic_cast<Derived*>(basePtr) successfully casts basePtr to a Derived*, and the method derivedMethod() can be called. If basePtr did not point to a Derived object, the cast would fail and derivedPtr would be nullptr.

Conditions for Using RTTI

  1. Polymorphism Requirement: RTTI (dynamic_cast and typeid) works only with polymorphic types—classes that have at least one virtual function. If a class does not have any virtual functions, RTTI features like dynamic_cast will not work correctly.
  2. Performance Overhead: Using RTTI, especially dynamic_cast, incurs a small runtime performance penalty because the system has to track type information and verify casts at runtime.

Summary of RTTI Components

  • typeid: Used to get type information of an object at runtime. Returns a type_info object.
  • dynamic_cast: Used to safely cast pointers or references between base and derived classes. Ensures the cast is valid at runtime, avoiding unsafe conversions.