I am a magnificent class!!!
I hold the character P and the integer 52!
Yes, you're really magnificent.
Now processing type Superb.
I am a superb class!!
I hold the superb value of 37!
Now processing type Grand.
I am a grand class!
Now processing type Superb.
I am a superb class!!
I hold the superb value of 18!
Now processing type Grand.
I am a grand class!
As with the preceding example, the exact output will vary from run to run because the program uses rand() to select types. Also some compilers may provide different output when name() is called, for example, 5Grand instead of Grand.
Misusing RTTI
RTTI has many vocal critics within the C++ community. They view RTTI as unnecessary, a potential source of program inefficiency, and a possible contributor to bad programming practices. Without delving into the debate over RTTI, let’s look at the sort of programming that you should avoid.
Consider the core of Listing 15.17:
Grand * pg;
Superb * ps;
for (int i = 0; i < 5; i++)
{
pg = GetOne();
pg->Speak();
if( ps = dynamic_cast
ps->Say();
}
By using typeid and ignoring dynamic_cast and virtual functions, you can rewrite this code as follows:
Grand * pg;
Superb * ps;
Magnificent * pm;
for (int i = 0; i < 5; i++)
{
pg = GetOne();
if (typeid(Magnificent) == typeid(*pg))
{
pm = (Magnificent *) pg;
pm->Speak();
pm->Say();
}
else if (typeid(Superb) == typeid(*pg))
{
ps = (Superb *) pg;
ps->Speak();
ps->Say();
}
else
pg->Speak();
}
Not only is this uglier and longer than the original, it has the serious flaw of naming each class explicitly. Suppose, for example, that you find it necessary to derive an Insufferable class from the Magnificent class. And suppose the new class redefines Speak() and Say(). With the version that uses typeid to test explicitly for each type, you would have to modify the for loop code, adding a new else if section. The original version, however, requires no changes at all. The following statement works for all classes derived from Grand:
pg->Speak();
And this statement works for all classes derived from Superb:
if( ps = dynamic_cast
ps->Say();
Tip
If you find yourself using typeid in an extended series of if else statements, you should check whether you should instead use virtual functions and dynamic_cast.
Type Cast Operators
The C type cast operator, in Bjarne Stroustrup’s view, is too lax. For example, consider the following:
struct Data
{
double data[200];
};
struct Junk
{
int junk[100];
};
Data d = {2.5e33, 3.5e-19, 20.2e32};
char * pch = (char *) (&d); // type cast #1 – convert to string
char ch = char (&d); // type cast #2 - convert address to a char
Junk * pj = (Junk *) (&d); // type cast #3 - convert to Junk pointer
First, which of these three type casts makes any sense? Unless you resort to the implausible, none of them make much sense. Second, which of these three type casts are allowed? In C, all of them are. Stroustrup’s response to this laxity was to tighten up what is allowable for a general type cast and to add four type cast operators that provide more discipline for the casting process:
dynamic_cast
const_cast
static_cast
reinterpret_cast
Instead of using a general type cast, you can select an operator that is suited to a particular purpose. This documents the intended reason for the type cast and gives the compiler a chance to check that you did what you thought you did.
You’ve already seen the dynamic_cast operator. To summarize, suppose High and Low are two classes, that ph is type High *, and that pl is type Low *. Then the following statement assigns a Low * pointer to pl only if Low is an accessible base class (direct or indirect) to High:
pl = dynamic_cast
Otherwise, the statement assigns the null pointer to pl. In general, the operator has this syntax:
dynamic_cast <
The purpose of this operator is to allow upcasts within a class hierarchy (such type casts being safe because of the
The const_cast operator is for making a type cast with the sole purpose of changing whether a value is const or volatile. It has the same syntax as the dynamic_cast operator:
const_cast <
The result of making such a type cast is an error if any other aspect of the type is altered. That is,
High bar;
const High * pbar = &bar
...
High * pb = const_cast
const Low * pl = const_cast
The first type cast makes *pb a pointer that can be used to alter the value of the bar object; it removes the const label. The second type cast is invalid because it attempts to change the type from const High * to const Low *.