Run-time type information

In computer programming, run-time type information or run-time type identification (RTTI)[1] is a feature of the C++ programming language that exposes information about an object's data type at runtime. Run-time type information can apply to simple data types, such as integers and characters, or to generic types. This is a C++ specialization of a more general concept called type introspection. Similar mechanisms are also known in other programming languages, such as Object Pascal (Delphi).

In the original C++ design, Bjarne Stroustrup did not include run-time type information, because he thought this mechanism was often misused.[2]

Overview

In C++, RTTI can be used to do safe typecasts, using the dynamic_cast<> operator, and to manipulate type information at runtime, using the typeid operator and std::type_info class.

RTTI is available only for classes that are polymorphic, which means they have at least one virtual method. In practice, this is not a limitation because base classes must have a virtual destructor to allow objects of derived classes to perform proper cleanup if they are deleted from a base pointer.

RTTI is optional with some compilers; the programmer can choose at compile time whether to include the functionality. There may be a resource cost to making RTTI available even if a program does not use it.

typeid

The typeid keyword is used to determine the class of an object at run time. It returns a reference to std::type_info object, which exists until the end of the program.[3] The use of typeid, in a non-polymorphic context, is often preferred over dynamic_cast<class_type> in situations where just the class information is needed, because typeid is always a constant-time procedure, whereas dynamic_cast may need to traverse the class derivation lattice of its argument at runtime. Some aspects of the returned object are implementation-defined, such as std::type_info::name(), and cannot be relied on across compilers to be consistent.

Objects of class std::bad_typeid are thrown when the expression for typeid is the result of applying the unary * operator on a null pointer. Whether an exception is thrown for other null reference arguments is implementation-dependent. In other words, for the exception to be guaranteed, the expression must take the form typeid(*p) where p is any expression resulting in a null pointer.

Example

#include <iostream>
#include <typeinfo>

class Person {
public:
    virtual ~Person() = default;
};

class Employee : public Person {};

int main() {
    Person person;
    Employee employee;
    Person* ptr = &employee;
    Person& ref = employee;
    
    // The string returned by typeid::name is implementation-defined.
    std::cout << typeid(person).name()
              << std::endl;  // Person (statically known at compile-time).
    std::cout << typeid(employee).name()
              << std::endl;  // Employee (statically known at compile-time).
    std::cout << typeid(ptr).name()
              << std::endl;  // Person* (statically known at compile-time).
    std::cout << typeid(*ptr).name()
              << std::endl;  // Employee (looked up dynamically at run-time
                             //           because it is the dereference of a
                             //           pointer to a polymorphic class).
    std::cout << typeid(ref).name()
              << std::endl;  // Employee (references can also be polymorphic)

    Person* p = nullptr;
    
    try {
        typeid(*p); // Not undefined behavior; throws std::bad_typeid.
    } catch (...) { }

    Person& p_ref = *p; // Undefined behavior: dereferencing null
    typeid(p_ref);      // does not meet requirements to throw std::bad_typeid
                        // because the expression for typeid is not the result
                        // of applying the unary * operator.
}

Output (exact output varies by system and compiler):

Person
Employee
Person*
Employee
Employee

dynamic_cast and Java cast

The dynamic_cast operator in C++ is used for downcasting a reference or pointer to a more specific type in the class hierarchy. Unlike the static_cast, the target of the dynamic_cast must be a pointer or reference to class. Unlike static_cast and C-style typecast (where type check is made during compilation), a type safety check is performed at runtime. If the types are not compatible, an exception will be thrown (when dealing with references) or a null pointer will be returned (when dealing with pointers).

A Java typecast behaves similarly; if the object being cast is not actually an instance of the target type, and cannot be converted to one by a language-defined method, an instance of java.lang.ClassCastException will be thrown.[4]

Example

Suppose some function takes an object of type A as its argument, and wishes to perform some additional operation if the object passed is an instance of B, a subclass of A. This can be accomplished using dynamic_cast as follows.

#include <array>
#include <iostream>
#include <memory>
#include <typeinfo>

using namespace std;

class A {
public:
    // Since RTTI is included in the virtual method table there should be at
    // least one virtual function.
    virtual ~A() = default;

    void MethodSpecificToA() {
        cout << "Method specific for A was invoked" << endl;
    }
};

class B: public A {
public:
    void MethodSpecificToB() {
        cout << "Method specific for B was invoked" << endl;
    }
};

void MyFunction(A& my_a) {
    try {
        // Cast will be successful only for B type objects.
        B& my_b = dynamic_cast<B&>(my_a);
        my_b.MethodSpecificToB();
    } catch (const bad_cast& e) {
        cerr << " Exception " << e.what() << " thrown." << endl;
        cerr << " Object is not of type B" << endl;
    }
}

int main() {
    array<unique_ptr<A>, 3> array_of_a; // Array of pointers to base class A.
    array_of_a[0] = make_unique<B>();   // Pointer to B object.
    array_of_a[1] = make_unique<B>();   // Pointer to B object.
    array_of_a[2] = make_unique<A>();   // Pointer to A object.

    for (int i = 0; i < 3; ++i)
        MyFunction(*array_of_a[i]);
}

Console output:

Method specific for B was invoked
Method specific for B was invoked
Exception std::bad_cast thrown.
Object is not of type B

A similar version of MyFunction can be written with pointers instead of references:

void MyFunction(A* my_a) {
    B* my_b = dynamic_cast<B*>(my_a);

    if (my_b != nullptr)
        my_b->methodSpecificToB();
    else
        std::cerr << "  Object is not B type" << std::endl;
}

See also

References

  1. Sun Microsystems (2000). "Runtime Type Identification". C++ Programming Guide. Oracle. Retrieved 16 April 2015.
  2. Bjarne Stroustrup (March 1993). "A History of C++: 1979—1991" (PDF). Bjarne Stroustrup. p. 50. Retrieved 2009-05-18.
  3. C++ standard (ISO/IEC14882) section 5.2.8 [expr.typeid], 18.5.1 [lib.type.info] -- http://cs.nyu.edu/courses/fall11/CSCI-GA.2110-003/documents/c++2003std.pdf
  4. http://docs.oracle.com/javase/8/docs/api/java/lang/ClassCastException.html
This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.