I l@ve RuBoard |
![]() ![]() |
5.10 Run-Time Type IdentificationOur implementation of what_am_i() has each class providing a virtual instance that returns a string literal identifying the class: class Fibonacci : public num_sequence { public: virtual const char* what_am_i() const { return "Fibonacci"; } // ... }; An alternative design is to provide a single num_sequence instance of what_am_i() that each derived class reuses through inheritance. This design frees each derived class from having to provide its own instance of what_am_i(). One way to implement this might be to add a string member to num_sequence. Each derived class constructor would then pass its class name as an argument to the num_sequence constructor. For example, inline Fibonacci:: Fibonacci( int len, int beg_pos ) : num_sequence( len, beg_pos, &_elems, "Fibonacci" ) {} An alternative implementation is to use the typeid operator. The typeid operator is part of the run-time type identification (RTTI) language support. It allows us to ask a polymorphic class pointer or reference the actual type of the object it refers to. #include <typeinfo> inline const char* num_sequence:: what_am_i() const { return typeid( *this ).name(); } To use the typeid operator, we must include the typeinfo header file. The typeid operator returns a type_info class object. This object stores type information. There is one associated with each polymorphic class, such as the Fibonacci and Pell derived classes. name() returns a const char* representation of the class name. The expression typeid( *this ) returns the type_info class object associated with the actual class type addressed by the this pointer within who_am_i(). The type_info name() member function is invoked through this object, returning the name of the actual class type. The type_info class also supports equality and inequality comparisons. For example, the following code determines whether ps addresses a Fibonacci class object: num_sequence *ps = &fib; // ... if ( typeid( *ps) == typeid( Fibonacci )) // ok, ps addresses a Fibonacci class object If we write ps->gen_elems( 64 ); we know that the Fibonacci instance of gen_elems() will be invoked. However, although we know from this test that ps addresses a Fibonacci class object, an attempt to invoke the Fibonacci instance of gen_elems() directly through ps results in a compile-time error: // error: ps is not a pointer to Fibonacci // although we know it currently addresses // a Fibonacci class object ps->Fibonacci::gen_elems( 64 ); ps does not "know" the type of the object it addresses, even if we and the typeid and virtual function mechanisms do. To invoke the Fibonacci instance of gen_elems(), we must instruct the compiler to convert ps into a pointer of type Fibonacci. The static_cast operator performs the conversion unconditionally. if ( typeid( *ps) == typeid( Fibonacci )) { Fibonacci *pf = static_cast<Fibonacci*>( ps ); pf->gen_elems( 64 ); } A static_cast is potentially dangerous because the compiler does not confirm that our conversion is correct. This is why I've couched its use in the truth condition of the typeid operator. A conditional conversion is provided by the dynamic_cast operator: if ( Fibonacci *pf = dynamic_cast<Fibonacci*>( ps )) pf->gen_elems( 64 ); The dynamic_cast operator is another RTTI operator. It performs a run-time verification that the object addressed by ps is actually of the Fibonacci class type. If it is, the conversion is carried out; pf now addresses the Fibonacci object. If it is not, the dynamic_cast operator returns 0. The if statement condition fails, and the static invocation of the Fibonacci instance of gen_elems() is not carried out. For a more detailed discussion of the C++ run-time type identification mechanism see Section 19.1 of [LIPPMAN98]. Exercise 5.1Implement a two-level stack hierarchy. The base class is a pure abstract Stack class that minimally supports the following interface: pop(), push(), size(), empty(), full(), peek(), and print(). The two concrete derived classes are LIFO_Stack and Peekback_Stack. The Peekback_Stack allows the user to retrieve the value of any element in the stack without modifying the stack itself. Exercise 5.2Reimplement the class hierarchy of Exercise 5.1 so that the base Stack class implements the shared, type-independent members. Exercise 5.3A type/subtype inheritance relationship in general reflects an is-a relationship: A range-checking ArrayRC is a kind of Array, a Book is a kind of LibraryRentalMaterial, an AudioBook is a kind of Book, and so on. Which of the following pairs reflects an is-a relationship? (a) member function isA_kindOf function (b) member function isA_kindOf class (c) constructor isA_kindOf member function (d) airplane isA_kindOf vehicle (e) motor isA_kindOf truck (f) circle isA_kindOf geometry (g) square isA_kindOf rectangle (h) automobile isA_kindOf airplane (i) borrower isA_kindOf library Exercise 5.4A library supports the following categories of lending materials, each with its own check-out and check-in policy. Organize these into an inheritance hierarchy: book audio book record children's puppet video Sega video game rental book Sony Playstation video game CD-ROM book Nintendo video game ![]() |
I l@ve RuBoard |
![]() ![]() |