I l@ve RuBoard |
![]() ![]() |
4.11 Pointers to Class Member FunctionsThe classes supporting the Fibonacci, Pell, Lucas, Square, and Pentagonal sequences are identical to those of the Triangular class except for the algorithm to generate the element sequence. In Chapter 5 we organize these classes into an object-oriented class hierarchy. In this section, we implement a general sequence class, num_sequence, to support all six sequences in a single class object. Here is our main() program: int main() { num_sequence ns; const int pos = 8; for ( int ix = 1; ix < num_sequence::num_of_sequences(); ++ix ) { ns.set_sequence( num_sequence::nstype( ix )); int elem_val = ns.elem( pos ); display( cout, ns, pos, elem_val ); } } ns is our general sequence class object. With each iteration of the for loop, we reset ns to represent a different numeric sequence using set_sequence() and the ns_type() return value. num_of_sequences() returns a count of the numeric sequences currently supported. Both num_of_sequences() and ns_type() are inline static member functions. elem() returns the element at the requested position. When the program is compiled and executed, it generates the following output: The element at position 8 for the fibonacci sequence is 21 The element at position 8 for the pell sequence is 408 The element at position 8 for the lucas sequence is 47 The element at position 8 for the triangular sequence is 36 The element at position 8 for the square sequence is 64 The element at position 8 for the pentagonal sequence is 92 The key to the design of the num_sequence class is the pointer to member function facility. A pointer to member function looks much the same as a pointer to nonmember function (introduced in Section 2.8). Both of them specify the return type and parameter list. A pointer to member function, however, must also indicate the class to which it is a member. For example, void (num_sequence::*pm)( int ) = 0; declares pm to be a pointer to member function of the num_sequence class. The member function pm addresses must have a return type of void and must take a single parameter of type int. pm is initialized to 0, indicating that it does not currently address a member function. If the syntax seems overly complex, we can hide it behind a typedef. For example, typedef void (num_sequence::*PtrType)( int ); PtrType pm = 0; declares PtrType as an alternative typedef name for a pointer to member function of the num_sequence class returning void and taking a single parameter of type int. The two declarations of pm are equivalent. The six numeric sequences are the same except for the algorithm to generate the sequence elements. The num_sequence class provides the following six member functions, each of which can be addressed by our PtrType pointer to member function: class num_sequence { public: typedef void (num_sequence::*PtrType)( int ); // _pmf addresses one of these void fibonacci( int ); void pell( int ); void lucas( int ); void triangular( int ); void square( int ); void pentagonal( int ); // ... private: PtrType _pmf; }; To take the address of a class member function, we apply the address-of operator (&) to the member function name qualified by its class scope operator. The return type and parameter list of the function are not specified. For example, to define and initialize a pointer to member to the fibonacci() member function, we write PtrType pm = &num_sequence::fibonacci; Similarly, to assign pm, we write pm = &num_sequence::triangular; Each invocation of set_sequence() assigns _pmf the address of one of the six functions. For simplicity, we can store the addresses of the six member functions in a static array. To prevent recalculating the elements of each sequence, we also keep a static vector of six element vectors: class num_sequence { public: typedef void (num_sequence::*PtrType)( int ); // ... private: vector<int>* _elem; // points to the current vector PtrType _pmf; // points to the current algorithm static const int num_seq = 7; //! static PtrType func_tbl[ num_seq ]; static vector<vector<int> > seq; }; The most complicated member definition here is that of seq: static vector<vector<int> > seq; This says that seq is a vector in which each element is a vector of integer elements. For example, it can hold the element vectors of each of our six numeric sequences. If we forget the space between the two greater-than symbols, // does not compile! static vector<vector<int>> seq; the definition does not compile. This is because of the maximal munch compilation rule. This rule requires that a symbol sequence always be interpreted as the maximal legal sequence of symbols. Because >> is a legal operator sequence, the two symbols are always grouped together in the absence of the space. Similarly, when we write a+++p, under the maximal munch rule it is always interpreted as a++ + p We must provide a definition of each static data member. Because PtrType is a nested type, any reference to it outside the num_sequence class must be qualified with the class scope operator. The value of num_seq is specified within the class definition, so we do not repeat it here. const int num_sequence::num_seq; vector<vector<int> > num_sequence::seq( num_seq ); num_sequence::PtrType num_sequence::func_tbl[ num_seq ] = { 0, &num_sequence::fibonacci, &num_sequence::pell, &num_sequence::lucus, &num_sequence::triangular, &num_sequence::square, &num_sequence::pentagonal }; If you find the nested type syntax confusing, you can hide it with a typedef: typedef num_sequence::PtrType PtrType; PtrType num_sequence::func_tbl[ num_seq ] = ... _elem and _pmf are set as a unit within set_sequence(). _elem addresses the vector that holds the elements for the numeric sequence. _pmf, of course, addresses the member function to generate additional elements of that sequence. (The actual implementation of set_sequence() is deferred until Section 5.2.) Unlike a pointer to function, a pointer to member function must be invoked through an object of the member function's class. The object becomes the this pointer of the member function that is invoked. For example, suppose we are given the following definitions: num_sequence ns; num_sequence *pns = &ns; PtrType pm = &num_sequence::fibonacci; To invoke pmf through ns, we write // equivalent to ns.fibonacci( pos ) (ns.*pm)( pos ) The .* pair of symbols is the pointer to member selection operator for class objects. The parentheses are necessary for it to be evaluated correctly. The pointer to member selection operator for a pointer to class operator is the ->* pair of symbols: // equivalent to pns->fibonacci( pos ) (pns->*pm)( pos ) Following is the implementation of elem(). If the position requested by the user is valid and if the number of elements currently stored does not include that position, the necessary elements are generated through an invocation of the function addressed by _pmf. int num_sequence::elem( int pos ) { if ( ! check_integrity( pos )) return 0; if ( pos > _elem->size() ) ( this->*_pmf )( pos-1 ); return (*_elem)[ pos-1 ]; } This is all the implementation we need to discuss at this point. In Chapter 5 we look at the programming details that allow each num_sequence class object to know its numeric sequence type at any point in its lifetime. Then we look at simpler ways to support these kinds of multiple type manipulations through object-oriented programming. Exercise 4.3Consider the following global data: string program_name; string version_stamp; int version_number; int tests_run; int tests_passed; Write a class to wrap around this data. Exercise 4.4A user profile consists of a login, the actual user name, the number of times logged on, the number of guesses made, the number of correct guesses, the current level ?one of beginner, intermediate, advanced, or guru ?and the percentage correct (this latter may be computed or stored). Provide a UserProfile class. Support input and output, equality and inequality. The constructors should allow for a default user level and default login name of "guest." How might you guarantee that each guest login for a particular session is unique? Exercise 4.5Implement a 4x4 Matrix class supporting at least the following general interface: addition and multiplication of two Matrix objects, a print() member function, a compound += operator, and subscripting supported through a pair of overloaded function call operators, as follows: float& operator()( int row, int column ); float operator()( int row, int column ) const; Provide a default constructor taking an optional 16 data values and a constructor taking an array of 16 elements. You do not need a copy constructor, copy assignment operator, or destructor for this class (these are required in Chapter 6 when we reimplement the Matrix class to support arbitrary rows and columns). ![]() |
I l@ve RuBoard |
![]() ![]() |