18.3. Pointer to Class MemberWe know that, given a pointer to an object, we can fetch a given member from that object using the arrow (->) operator. Sometimes it is useful to start from the member. That is, we may want to obtain a pointer to a specific member and then fetch that member from one or another object. We can do so by using a special kind of pointer known as a pointer to member. A pointer to member embodies the type of the class as well as the type of the member. This fact impacts how pointers to member are defined, how they are bound to a function or data member, and how they are used. Pointers to member apply only to nonstatic members of a class. static class members are not part of any object, so no special syntax is needed to point to a static member. Pointers to static members are ordinary pointers. 18.3.1. Declaring a Pointer to MemberIn exploring pointers to members, we'll use a simplified version of the Screen class from Chapter 12. class Screen { public: typedef std::string::size_type index; char get() const; char get(index ht, index wd) const; private: std::string contents; index cursor; index height, width; }; Defining a Pointer to Data MemberThe contents member of Screen has type std::string. The complete type of contents is "member of class Screen, whose type is std::string." Consequently, the complete type of a pointer that could point to contents is "pointer to member of class Screen of type std::string." This type is written as string Screen::* We can define a pointer to a string member of class Screen as string Screen::*ps_Screen; ps_Screen could be initialized with the address of contents by writing string Screen::*ps_Screen = &Screen::contents; We could also define a pointer that might address the height, width, or cursor members as Screen::index Screen::*pindex; which says that pindex is a pointer to a member of class Screen with type Screen::index. We could assign the address of width to this pointer as follows: pindex = &Screen::width; The pointer pindex can be set to any of width, height, or cursor because all three are Screen class data members of type index. Defining a Pointer to Member FunctionA pointer to a member function must match the type of the function to which it points, in three ways:
A pointer to member function is defined by specifying the function return type, parameter list, and a class. For example, a pointer to a Screen member function capable of referring to the version of get that takes no parameters has the following type: char (Screen::*)() const This type specifies a pointer to a const member function of class Screen, taking no parameters and returning a value of type char. A pointer to this version of get can be defined and initialized as follows: // pmf points to the Screen get member that takes no arguments char (Screen::*pmf)() const = &Screen::get; We might also define a pointer to the two-parameter version of get as char (Screen::*pmf2)(Screen::index, Screen::index) const; pmf2 = &Screen::get;
// error: non-member function p cannot have const qualifier char Screen::*p() const; Using Typedefs for Member PointersTypedefs can make pointers to members easier to read. For example, the following typedef defines Action to be an alternative name for the type of the two-parameter version of get: // Action is a type name typedef char (Screen::*Action)(Screen::index, Screen::index) const; Action is the name of the type "pointer to a const member function of class Screen taking two parameters of type index and returning char." Using the typedef, we can simplify the definition of a pointer to get as follows: Action get = &Screen::get; A pointer-to-member function type may be used to declare function parameters and function return types: // action takes a reference to a Screen and a pointer to Screen member function Screen& action(Screen&, Action = &Screen::get); This function is declared as taking two parameters: a reference to a Screen object and a pointer to a member function of class Screen taking two index parameters and returning a char. We could call action either by passing it a pointer or the address of an appropriate member function in Screen: Screen myScreen; // equivalent calls: action(myScreen); // uses the default argument action(myScreen, get); // uses the variable get that we previously defined action(myScreen, &Screen::get); // pass address explicitly
18.3.2. Using a Pointer to Class MemberAnalogous to the member access operators, operators. and ->, are two new operators, .* and .->, that let us bind a pointer to member to an actual object. The left-hand operand of these operators must be an object of or pointer to the class type, respectively. The right-hand operand is a pointer to a member of that type:
Using a Pointer to Member FunctionUsing a pointer to member, we could call the version of get that takes no parameters as follows: // pmf points to the Screen get member that takes no arguments char (Screen::*pmf)() const = &Screen::get; Screen myScreen; char c1 = myScreen.get(); // call get on myScreen char c2 = (myScreen.*pmf)(); // equivalent call to get Screen *pScreen = &myScreen; c1 = pScreen->get(); // call get on object to which pScreen points c2 = (pScreen->*pmf)(); // equivalent call to get
Without the parentheses, myScreen.*pmf() would be interpreted to mean myScreen.*(pmf()) This code says to call the function named pmf and bind its return value to the pointer to member object operator (.*). Of course, the type of pmf does not support such a use, and a compile-time error would be generated. As with any other function call, we can also pass arguments in a call made through a pointer to member function: char (Screen::*pmf2)(Screen::index, Screen::index) const; pmf2 = &Screen::get; Screen myScreen; char c1 = myScreen.get(0,0); // call two-parameter version of get char c2 = (myScreen.*pmf2)(0,0); // equivalent call to get Using a Pointer to Data MemberThe same pointer-to-member operators are used to access data members: Screen::index Screen::*pindex = &Screen::width; Screen myScreen; // equivalent ways to fetch width member of myScreen Screen::index ind1 = myScreen.width; // directly Screen::index ind2 = myScreen.*pindex; // dereference to get width Screen *pScreen; // equivalent ways to fetch width member of *pScreen ind1 = pScreen->width; // directly ind2 = pScreen->*pindex; // dereference pindex to get width Pointer-to-Member Function TablesOne common use for function pointers and for pointers to member functions is to store them in a function table. A function table is a collection of function pointers from which a given call is selected at run time. For a class that has several members of the same type, such a table can be used to select one from the set of these members. Let's assume that our Screen class is extended to contain several member functions, each of which moves the cursor in a particular direction: class Screen { public: // other interface and implementation members as before Screen& home(); // cursor movement functions Screen& forward(); Screen& back(); Screen& up(); Screen& down(); }; Each of these new functions takes no parameters and returns a reference to the Screen on which it was invoked. Using the Function-Pointer TableWe might want to define a move function that could call any one of these functions and perform the indicated action. To support this new function, we'll add a static member to Screen that will be an array of pointers to the cursor movement functions: class Screen { public: // other interface and implementation members as before // Action is pointer that can be assigned any of the cursor movement members typedef Screen& (Screen::*Action)(); static Action Menu[]; // function table public: // specify which direction to move enum Directions { HOME, FORWARD, BACK, UP, DOWN }; Screen& move(Directions); }; The array named Menu will hold pointers to each of the cursor movement functions. Those functions will be stored at the offsets corresponding to the enumerators in Directions. The move function takes an enumerator and calls the appropriate function: Screen& Screen::move(Directions cm) { // fetch the element in Menu indexed by cm // run that member on behalf of this object (this->*Menu[cm])(); return *this; } The call inside move is evaluated as follows: The Menu element indexed by cm is fetched. That element is a pointer to a member function of the Screen class. We call the member function to which that element points on behalf of the object to which this points. When we call move, we pass it an enumerator that indicates which direction to move the cursor: Screen myScreen; myScreen.move(Screen::HOME); // invokes myScreen.home myScreen.move(Screen::DOWN); // invokes myScreen.down Defining a Table of Member Function PointersWhat's left is to define and initialize the table itself: Screen::Action Screen::Menu[] = { &Screen::home, &Screen::forward, &Screen::back, &Screen::up, &Screen::down, }; ![]() |