19.10.  Runtime Type Identification (RTTI)

[ fromfile: rtti.xml id: rtti ]

Abstract

This section covers dynamic_cast and typeid, two operators that enable runtime type identification (RTTI).

The conversion of a base class pointer to a derived class pointer is called downcasting because casting from the base class to a derived class is considered moving down the class hierarchy.

When operating on hierarchies of types, sometimes it is necessary to downcast a pointer to a more specific type. Without a downcast, only the interface of the pointer type (the base class) is available. One common situation where downcasting is used is inside functions that accept base class pointers.

RTTI enables programmers to safely convert pointers and references to objects from base to derived types.

dynamic_cast<D*>(ptr) takes two operands: a pointer type D* and a pointer ptr of a polymorphic type B*. If D is a base class of B (or if B is the same as D) dynamic_cast<D*>(ptr) is an upcast (or not a cast at all) and is equivalent to static_cast<D*>(ptr). But if ptr has the address of an object of type D, where D is derived from B, the operator returns a downcast pointer of type D*, pointing to the same object. If the cast is not possible, a null pointer is returned.

dynamic_cast performs runtime checking to determine whether the pointer/reference conversion is valid. For example, suppose you are processing a collection of QWidget*. Example 19.20 shows operations on a collection of QWidgets. Further suppose that you want to operate only on buttons and spinboxes, leaving the other widgets alone.

Example 19.20. src/rtti/dynamic_cast.cpp

[ . . . . ]
int processWidget(QWidget* wid) {
    
    if (wid->inherits("QAbstractSpinBox")) { 1
        QAbstractSpinBox* qasbp = 
            static_cast <QAbstractSpinBox*> (wid);
        qasbp->setAlignment(Qt::AlignHCenter);
    }
    else {
        QAbstractButton* buttonPtr = 
            dynamic_cast<QAbstractButton*>(wid);
        if (buttonPtr) {                     2
            buttonPtr->click();
            qDebug() << QString("I clicked on the %1 button:")
                .arg(buttonPtr->text());
        }
        return 1;
    }
    return 0;
}
[ . . . . ]
    QVector<QWidget*> widvect;

    widvect.append(new QPushButton("Ok"));
    widvect.append(new QCheckBox("Checked"));
    widvect.append(new QComboBox());
    widvect.append(new QMenuBar());
    widvect.append(new QCheckBox("With Fries"));
    widvect.append(new QPushButton("Nooo!!!!"));
    widvect.append(new QDateTimeEdit());
    widvect.append(new QDoubleSpinBox());
    foreach (QWidget* widpointer, widvect) {
        processWidget(widpointer);
    }
   return 0;
}

1

Only for QObjects processed by moc.

2

If non-null, it's a valid QAbstractButton.


[Note] Note

qobject_cast (Section 12.2) is faster than dynamic_cast but only works on QObject-derived types.

In terms of runtime cost, dynamic_cast is considerably more expensive, perhaps 10 to 50 times the cost of a static_cast. However, they are not interchangable operations and are used in different situations.