Section 18.3. Pointer to Class Member


18.3. Pointer to Class Member

We 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.

Exercises Section 18.2.4

Exercise 18.20:

Given the following class hierarchy in which each class defines a public default constructor and virtual destructor, which type name do the following statements print?

      class A { /* ... */ };      class B : public  A { /* ... */ };      class C : public  B { /* ... */ };      (a) A *pa = new C;          cout << typeid(pa).name() << endl;      (b) C cobj;          A& ra = cobj;          cout << typeid(&ra).name() << endl;      (c) B *px = new B;          A& ra = *px;          cout << typeid(ra).name() << endl; 


18.3.1. Declaring a Pointer to Member

In 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 Member

The 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 Function

A pointer to a member function must match the type of the function to which it points, in three ways:

  1. The type and number of the function parameters, including whether the member is const

  2. The return type

  3. The class type of which it is a member

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; 

The precedence of the call operator is higher than that of the pointer-to-member operators. Therefore, the parentheses around Screen::* are essential. Without them, the compiler treats the following as an (invalid) function declaration:


      // error: non-member function p cannot have const qualifier      char Screen::*p() const; 


Using Typedefs for Member Pointers

Typedefs 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 

Exercises Section 18.3.1

Exercise 18.21:

What is the difference between an ordinary data or function pointer and a pointer to data or function member?

Exercise 18.22:

Define the type that could represent a pointer to the isbn member of the Sales_item class.

Exercise 18.23:

Define a pointer that could point to the same_isbn member.

Exercise 18.24:

Write a typedef that is a synonym for a pointer that could point to the avg_price member of Sales_item.


18.3.2. Using a Pointer to Class Member

Analogous 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:

  • The pointer-to-member dereference operator (.*) fetches the member from an object or reference.

  • The pointer-to-member arrow operator (->*) fetches the member through a pointer to an object.

Using a Pointer to Member Function

Using 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 

The calls (myScreen.*pmf)() and (pScreen->*pmf)() require the parentheses because the precedence of the call operator() is higher than the precedence of the pointer to member operators.



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 Member

The 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 Tables

One 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 Table

We 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 Pointers

What's left is to define and initialize the table itself:

      Screen::Action Screen::Menu[] = { &Screen::home,                                        &Screen::forward,                                        &Screen::back,                                        &Screen::up,                                        &Screen::down,                                      }; 



C++ Primer
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2006
Pages: 223
Authors: Stephen Prata

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