Team LiB
Previous Section Next Section

18.3. Pointer to Class Member

18.3. 类成员的指针

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.

成员指针只应用于类的非 static 成员。static 类成员不是任何对象的组成部分,所以不需要特殊语法来指向 static 成员,static 成员指针是普通指针。

18.3.1. Declaring a Pointer to Member

18.3.1. 声明成员指针

In exploring pointers to members, we'll use a simplified version of the Screen class from Chapter 12.

在研究成员指针时,将使用第十二章Screen 类的简化版本。

     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

Screen 类的 contents 成员的类型为 std::stringcontents 的完全类型是“Screen 类的成员,其类型是 std::string”。因此,可以指向 contents 的指针的完全类型是“指向 std::string 类型的 Screen 类成员的指针”,这个类型可写为

     string Screen::*

We can define a pointer to a string member of class Screen as

可以将指向 Screen 类的 string 成员的指针定义为

     string Screen::*ps_Screen;

ps_Screen could be initialized with the address of contents by writing

可以用 contents 的地址初始化 ps_Screen,代码为

     string Screen::*ps_Screen = &Screen::contents;

We could also define a pointer that might address the height, width, or cursor members as

还可以将指向 heightwidthcursor 成员的指针定义为

     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::index 类型的 Screen 类成员的指针。可以将 width 的地址赋给这个指针:

     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.

可以用指针 pindex 指向 widthheightcursor 中任意一个,因为这三个都是 index 类型的 Screen 类成员。

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

    函数形参的类型和数目,包括成员是否为 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:

通过指定函数返回类型、形参表和类来定义成员函数的指针。例如,可引用不接受形参的 get 版本的 Screen 成员函数的指针具有如下类型:

     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:

这个类型指定 Screen 类的 const 成员函数的指针,不接受形参并返回 char 类型的值。这个 get 函数版本的指针可以像下面这样定义和初始化:

     // 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

也可以将带两个形参的 get 函数版本的指针定义为

     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:

调用操作符的优先级高于成员指针操作符,因此,包围 Screen::* 的括号是必要的,没有这个括号,编译器就将下面代码当作(无效的)函数声明:


     // 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 定义为带两个形参的 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 是类型“Screen 类的接受两个 index 类型形参并返回 char 的成员函数的指针”的名字。使用类型别名,可以将 get 指针的定义简化为

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 对象的引用,以及 Screen 类的接受两个 index 类型形参并返回 char 的成员函数的指针。可以通过传递 Screen 类中适当成员函数的指针或地址调用 action 函数:

     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.

定义可以表示 Sales_item 类的 isbn 成员的指针的类型。

Exercise 18.23:

Define a pointer that could point to the same_isbn member.

定义可以指向 same_isbn 成员的指针。

Exercise 18.24:

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

编写类型别名,作为可指向 Sales_itemavg_price 成员的指针的同义词。


18.3.2. Using a Pointer to Class Member

18.3.2. 使用类成员指针

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:

使用成员指针,可以这样调用不带形参的 get 函数版本:

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

因为调用操作符(())比成员指针操作符优先级高,所以调用 (myScreen.*pmf)()(pScreen->*pmf)() 需要括号。



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.

这个代码是说,调用名为 pmf 的函数,并将它的返回值绑定到成员对象操作符(.*)的指针。当然,pmf 的类型不支持这样的使用,且会产生编译时错误。

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:

对具有几个相同类型成员的类而言,可以使用这样的表来从这些成员的集合中选择一个。假定扩展 Screen 类以包含几个成员函数,其中每一个在特定方向移动光标:

     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:

我们可能希望定义一个 move 函数,它可以调用这些函数中的任意一个并执行指定动作。为了支持这个新函数,我们将在 Screen 中增加一个 static 成员,该成员是光标移动函数的指针的数组:

     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:

名为 Menu 的数组将保存指向每个光标移动函数的指针,将在对应于 Directions 中枚举成员的偏移位置保存那些函数,move 函数接受枚举成员并调用适当函数:

     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.

这样计算 move 内部的调用:获取由 cm 索引的 Menu 元素(该元素是 Screen 类成员函数的指针),代表 this 指向的对象调用该元素指向的成员函数。

When we call move, we pass it an enumerator that indicates which direction to move the cursor:

调用 move 时,传给它一个枚举成员,指出向哪个方向移动光标:

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

Exercises Section 18.3.2

Exercise 18.25:

What is the type of the Screen class members screen and cursor?

Screen 类的成员 screencursor 的类型是什么?

Exercise 18.26:

Define a pointer to member that could point to the cursor member of class Screen. Fetch the value of Screen::cursor through that pointer.

定义一个可以指向 Screencursor 成员的成员指针,通过该指针获取 Screen::cursor 的值。

Exercise 18.27:

Define a typedef for each distinct type of Screen member function.

Screen 类成员函数的每个可区分类型定义类型别名。

Exercise 18.28:

Pointers to members may also be declared as class data members. Modify the Screen class definition to contain a pointer to a Screen member function of the same type as home and end.

也可以将成员指针声明为类的数据成员。修改 Screen 类的定义,以便包含与 homeend 类型相同的 Screen 成员函数的指针。

Exercise 18.29:

Write a Screen constructor that takes a parameter of type pointer to Screen member function whose parameter list and return type are the same as those for the member functions home and end.

编写一个 Screen 构造函数,该构造函数接受指向 Screen 成员函数的指针类型形参,成员函数的形参表和返回类型与成员函数 homeend 的相同。

Exercise 18.30:

Provide a default argument for this parameter. Use this parameter to initialize the data member introduced in the previous exercise.

为该形参提供默认实参,使用该形参对上题中引入的数据成员进行初始化。

Exercise 18.31:

Provide a Screen member function to set this member.

提供一个 Screen 成员函数来设置这个成员。


Team LiB
Previous Section Next Section