Recently, type_info, a meta-class that provides a limited amount of somewhat unreliable runtime type information, was added to the standard C++ library. The type_info class includes member functions for discovering the name of the type (name and raw_name), for comparing types (== and !=), and for determining the order of types in collating sequences (collating sequences are used by objects representing locales):
class type_info
{
public:
virtual ~type_info();
int operator==(const type_info&
rhs) const;
int operator!=(const type_info&
rhs) const;
int before(const type_info& rhs)
const;
const char* name() const;
const char* raw_name() const;
private:
...
};
The standard C++ library also provides a global operator named typeid() that expects any expression as input and returns a constant reference to a type_info object representing the expression's type:
const type_info& typeid(exp);
For example, assume we define the C++ version of our Note class:
class Note
{
public:
Note()
{
frequency = 60;
duration = 300;
}
virtual void play()
{
cout << "playing a
generic note" << endl;
}
private:
int frequency; // in Hz
int duration; // in millisecs
};
As in our Java version, HornNote and ViolinNote are defined as subclasses:
class HornNote: public Note
{
public:
void play()
{
cout << "playing a horn
note" << endl;
}
};
class ViolinNote: public Note
{
public:
void play()
{
cout << "playing a violin
note" << endl;
}
};
For testing purposes, we introduce a global function that displays the name of the type of note pointed at by its parameter:
void displayType(Note* note)
{
const type_info& tp = typeid(*note);
cout << "type = "
<< tp.name() << endl;
}
Notice that the value returned by typeid() must be stored in a constant reference variable.
Our test driver, main(), passes the same note pointer to displayType() three times. However, each time the pointer points to a different type of Note object:
Note* note = new Note();
displayType(note);
note = new HornNote();
displayType(note);
note = new ViolinNote();
displayType(note);
To get main() and displayType() to compile, we need the following include directives:
#include <typeinfo>
#include <iostream>
using namespace std;
Some compilers generate the necessary type information by default. To save space, other compilers, such as VC++, require programmers to set special compiler options before the necessary type information will be generated.[1]
It should also be mentioned that distinct type information for a subclass is generated by the typeid() operator only if the base class is polymorphic (i.e., contains at least one virtual function). Otherwise, the typeid() operator simply produces the type_info object of the base class.
Unfortunately, the output produced by the test program is compiler-dependent. The output produced by DJGPP, a GNU compiler for Windows, prefaces the type names by their lengths:
type = 4Note
type = 8HornNote
type = 10ViolinNote
The output produced by VC++ prefaces type names with the string "class":
type = class Note
type = class HornNote
type = class ViolinNote
The typeid() operator can also convert a type expression into its associated type_info object.
const type_info& typeid(type-exp);
We can compare this object to the type_info object of an expression using the == operator. This could be used to perform safe downcasts. For example, assume we add a special honk() function to our HornNote class:
class HornNote: public Note
{
public:
void honk()
{
cout << "HONK,
HONK!\n";
}
// etc.
};
Assume a test program uses a generic Note pointer to point at a HornNote object:
Note* note = 0;
// and later:
note = new HornNote();
Now assume we want to call the honk() function. An explicit downcast will be required, but we want to be sure note points to a HornNote before proceeding. Here's one way to do this:
if (typeid(*note) == typeid(HornNote))
((HornNote*)note)->honk();
else
cerr << "Error: unexpected
type\n";
Of course this is exactly what the dynamic_cast<> operator does:
HornNote* hornNote = dynamic_cast<HornNote*>(note);
if (hornNote)
hornNote->honk();
else
cerr << "Error: unexpected
type\n";
[1] In VC++, select the C/C++ tab in the dialog that appears when you select Settings from the Project menu. Pick "C++ Language" in the Category combo box. Check the "Enable Runtime Type Information (RTTI)" box