Team LiB
Previous Section Next Section

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,
                                     };

    Team LiB
    Previous Section Next Section