if(cm)
cm->special();
else
cout << "not a Investment" << endl;
}
cout << "cast from intermediate pointer:\n";
Security* sp = new Metal;
Investment* cp = dynamic_cast<Investment*>(sp);
if(cp) cout << " it's an Investment\n";
Metal* mp = dynamic_cast<Metal*>(sp);
if(mp) cout << " it's a Metal too!\n";
purge(portfolio);
} ///:~
This example is much shorter, since most of the code in the original example was just the overhead for checking the casts. The target type of a dynamic_cast is placed in angle brackets, like the other new-style C++ casts (static_cast, and so on), and the object to cast appears as the operand. dynamic_cast requires that the types you use it with be polymorphic if you want safe downcasts.[105] This in turn requires that the class must have at least one virtual function. Fortunately, the Security base class has a virtual destructor, so we didn’t have to invent some extraneous function to get the job done. dynamic_cast does its work at runtime, of course, since it has to check the virtual function table of objects according to there dynamic type. This naturally implies that dynamic_cast tends to be more expensive than the other new-style casts.
You can also use dynamic_cast with references instead of pointers, but since there is no such thing as a null reference, you need another way to know if the cast fails. That "other way" is to catch a bad_cast exception, as follows:
Metal m;
Security& s = m;
try {
Investment& c = dynamic_cast<Investment&>(s);
cout << " it's an Investment\n";
}
catch (bad_cast&) {
cout << "s is not an Investment type\n";
}
The bad_cast class is defined in the <typeinfo> header, and, like most of the standard library, is declared in the std namespace.
The typeid operator
The other way to get runtime information for an object is through the typeid operator. This operator returns an object of class type_info, which yields information about the type of object to which it was applied. If the type is polymorphic, it gives information about the most derived type that applies (the dynamic type); otherwise it yields static type information. One use of the typeid operator is to get the name of the dynamic type of an object as a const char*, as you can see in the following example.
//: C08:TypeInfo.cpp
// Illustrates the typeid operator
#include <iostream>
#include <typeinfo>
using namespace std;
struct PolyBase {virtual ~PolyBase(){}};
struct PolyDer : PolyBase {};
struct NonPolyBase {};
struct NonPolyDer : NonPolyBase {NonPolyDer(int){}};
int main() {
// Test polymorphic Types
const PolyDer pd;
const PolyBase* ppb = &pd;
cout << typeid(ppb).name() << endl;
cout << typeid(*ppb).name() << endl;
cout << boolalpha << (typeid(*ppb) == typeid(pd))
<< endl;
cout << (typeid(PolyDer) == typeid(const PolyDer))
<< endl;
// Test non-polymorphic Types
const NonPolyDer npd(1);
const NonPolyBase* nppb = &npd;
cout << typeid(nppb).name() << endl;
cout << typeid(*nppb).name() << endl;
cout << (typeid(*nppb) == typeid(npd))
<< endl;
// Test a built-in type
int i;
cout << typeid(i).name() << endl;
} ///:~
The output from this program is
struct PolyBase const *
struct PolyDer
true
true
struct NonPolyBase const *
struct NonPolyBase
false
int
The first output line just echoes the static type of ppb because it is a pointer. To get RTTI to kick in, you need to look at the object a pointer or reference is connected to, which is illustrated in the second line. Notice that RTTI ignores top-level const and volatile qualifiers. With non-polymorphic types, you just get the static type (the type of the pointer itself). As you can see, built-in types are also supported.
It turns out that you can’t store the result of a typeid operation in a type_info object, because there are no accessible constructors and assignment is disallowed; you must use it as we have shown. In addition, the actual string returned by type_info::name( ) is compiler dependent. Some compilers return "class C" instead of just "C", for instance, for a class named C. Applying typeid to an expression that dereferences a null pointer will cause a bad_typeid exception (also defined in <typeinfo>) to be thrown.
The following example shows that the class name that type_info::name( ) returns is fully qualified.
//: C08:RTTIandNesting.cpp
#include <iostream>
#include <typeinfo>
using namespace std;
class One {
class Nested {};
Nested* n;
public:
One() : n(new Nested) {}
~One() { delete n; }
Nested* nested() { return n; }
};
int main() {
One o;
cout << typeid(*o.nested()).name() << endl;
} ///:~
Since Nested is a member type of the One class, the result is One::Nested.
You can also ask a type_info object if it precedes another type_info object in the implementation-defined "collation sequence" (the native ordering rules for text), using before(type_info&), which returns true or false. When you say,.
if(typeid(me).before(typeid(you))) // ...
you’re asking if me occurs before you in the current collation sequence. This is useful should you use type_info objects as keys.
Casting to intermediate levels
As you saw in the earlier program that used the hierarchy of Security classes, dynamic_cast can detect both exact types and, in an inheritance hierarchy with multiple levels, intermediate types. Here is another example.
//: C08:IntermediateCast.cpp
#include <cassert>
#include <typeinfo>
using namespace std;
class B1 {
public:
virtual ~B1() {}
105
Compilers typically insert a pointer to a class’s RTTI table inside of its virtual function table.