16.11 RUN-TIME TYPE IDENTIFICATION IN C


16.11 RUN-TIME TYPE IDENTIFICATION IN C++

We will now answer the following question in the context of the role playing classes example of the previous section: Given an Employee object and given the fact that the active role of an employee is stored as a Role* pointer, how does one figure out the exact identity of the active role at run time? Or, for that matter, how does one figure out the "true" identities of the roles stored in the allRoles data member of the Employee class.

One approach would be to examine the value returned by the getRoleName() function when it is invoked on the role pointed to by the activeRole data member. However, C++ also makes available a couple of other approaches that come handy when we do not have a data member like roleName defined for a class such as Role. The first of these is based on Run-Time Type Identification (RTTI). If we wanted to check whether a given Role* is of type, say, Manager*, we can use the dynamic_cast operator made available by C++'s RTTI in the following manner

      Manager* m = dynamic_cast<Manager*>( role ); 

where role is of type Role*. If role's true identity is not Manager*, then dynamic_cast returns the null pointer. So in order to ascertain whether or not a role associated with an Employee is of type Manager*, all we have to do is to test whether or not m is a null pointer. Converting a base class pointer to a derived class pointer in this fashion is called downcasting. Using downcasting, we can write the kind of code that is shown for checkReadyForPromotion() in line (B) of the RolePlayers.cc program.

But note that dynamic casts work only for polymorphic type. As was mentioned in Chapter 15, a class is a polymorphic type if the class contains at least one virtual function. In the absence of any other virtual functions, we can always declare the destructor to be virtual.

In the function checkReadyForPromotion() in line (B) of the RolePlayers.cc program, we showed dynamic cast working through pointer variables. It can also work with object references, but then the operator dynamic_cast can only be invoked inside a try-catch block. This is demonstrated by the commented out code that starts in line (C) of the same program. The idea now is that if the dynamic_cast operator does not succeed, it would throw an exception. So in the catch block of the function we put the code that corresponds to the case when the true identity of the active role is not Manager. Note that now we are downcasting to Manager& and not to Manager*, hence the operator invocation with dynamic_cast<Manager&>. Also, the argument supplied to the casting operator is Role& and not Role*.

The RTTI feature of C++ also supports the static_cast operator that the reader has already seen for compile-time casts in earlier chapters. As was mentioned earlier, the operation static_cast<T>(e) converts a value e to type T provided an implicit conversion exists from the type of e to the type T. While, of course, a Derived* type can always be assigned directly to a Base* type, we could also do the conversion by using static_cast. The safety feature of static_cast is that it cannot cast away const. Yet another casting operator supported by RTTI is reinterpret_cast that is used primarily for converting an int into a memory address. Yet another cast operator is const_cast. The invocation const_cast<T>(e) removes the constness of e.

We have already pointed out that RTTI works only for the polymorphic types and, for a class to be of polymorphic type, at least one of its member functions must be virtual. A class derived from a polymorphic class is also polymorphic. Therefore, given a polymorphic class C at some level in a multilevel class hierarchy, all the direct and indirect subclasses of C will also act polymorphically.

We will illustrate this idea with a simple example shown in Figure 16.16. We will make X polymorphic by declaring its destructor virtual. The rest of the class hierarchy will be a straightforward derivation of Y from X and Z from Y:

 
//PolymorpicTypes.cc #include <iostream> using namespace std; class X { public: virtual ~X(){}; }; class Y : public X {}; class Z : public Y {}; int main() { Y* p = new Z(); Z* q = dynamic_cast<Z*>( p ); if ( q != 0 ) cout << "p was actually Z*" << endl; return 0; }

For dynamic_cast<Z*> to return a non-null pointer in main(), the class Y has to act polymorphically, which it does on account of the fact that the Y's parent class X was made polymorphic by declaring its destructor to be virtual. So the output of the program is "p was actually Z*".


Figure 16.16




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net