I l@ve RuBoard Previous Section Next Section

5.3 Polymorphism without Inheritance

The num_sequence class of Section 4.10 simulates polymorphism. Each class object can be made into any of the six numerical sequences at any point in the program through the set_sequence() member function:



for ( int ix = 1; ix < num_sequence::num_of_sequences(); ++ix ) 


{ 


      ns.set_sequence( num_sequence::nstype( ix )); 


      int elem_val = ns.elem( pos ); 


      // ... 


} 

The ability to change the sequence type of ns is supported through programming rather than through direct support of the language. Each class object contains an _isa data member that identifies the current numeric sequence that it represents:



class num_sequence { 


public: 


    // ... 





private: 


    vector<int> *_elem; // addresses current element vector 


    PtrType      _pmf;  // addresses current element generator 


    ns_type      _isa;  // identifies current sequence type 


    // ... 


}; 

_isa is set to a named constant value that represents one of the supported numeric sequence types. The constant values are grouped in an enumerated type I've named ns_type:



class num_sequence { 


public: 


    enum ns_type { 


        ns_unset, ns_fibonacci, ns_pell, ns_lucas, 


        ns_triangular, ns_square, ns_pentagonal 


    }; 





    // ... 


}; 

nstype() verifies that its integer parameter represents a valid numeric sequence value. If it does, it returns the associated enumerator; otherwise, it returns ns_unset:



class num_sequence { 


public: 


    // ... 





    static ns_type nstype( int num ) 


    { 


           return num <= 0 || num >= num_seq 


               ? ns_unset  // invalid value 


               : static_cast< ns_type >( num ); 


    } 


}; 

The static_cast is a special conversion notation. It converts the integer num to its associated ns_type enumerator. The result of nstype() is passed to set_sequence():



ns.set_sequence( num_sequence::nstype( ix )); 

set_sequence() does the work of setting _pmf, _isa, and _elem data members to the correct numeric sequence:



void num_sequence:: 


set_sequence( ns_type nst ) 


{ 


   switch ( nst ) 


   { 


      default: 


          cerr << "invalid type: setting to 0\n"; 


          // deliberate fall-through 





     case ns_unset: 


          _pmf = 0; 


          _elem = 0; 


          _isa = ns_unset; 


          break; 





     case ns_fibonacci:  case ns_pell:   case ns_lucas: 


     case ns_triangular: case ns_square: case ns_pentagonal: 


          // func_tbl: table of pointer to member functions 


          // seq: vector of vectors holding sequence elements 


          _pmf = func_tbl[ nst ]; 


          _elem = &seq[ nst ]; 


          _isa = nst; 


          break; 


   } 


} 

To support a query as to the numeric sequence to which an object is currently set, I provide a what_am_i() operation that returns a character string identifying the current numeric sequence. For example,



inline void display( ostream &os, const num_sequence &ns, int pos ) 


{ 


   os << "The element at position " 


      << pos << " for the " 


      << ns.what_am_i() << " sequence is " 


      << ns.elem( pos ) << endl; 


} 

what_am_i() indexes _isa into a static character string array that lists the supported numeric sequence names in the order of the ns_type enumerators:



const char* num_sequence:: 


what_am_i() const 


{ 


      static char *names[ num_seq ] = { 


           "notSet", 


           "fibonacci",  "pell", 


           "lucas",      "triangular", 


           "square",     "pentagonal" 


      }; 


   return names[ _isa ]; 


} 

This is quite a lot of work, particularly in terms of maintenance. Each time we wish to add to or delete a numeric sequence type, all the following must be updated correctly: the vector of element vectors, the array of pointer to member functions, the what_am_i() string array, the switch statement of set_sequence(), the value of num_seq, and so on. Under the object-oriented programming model, this sort of explicit programming overhead is eliminated, making our code simpler and more extensible.

    I l@ve RuBoard Previous Section Next Section