12.2. The Implicit this Pointer12.2. 隐含的 this 指针As we saw in Section 7.7.1 (p. 260), member functions have an extra implicit parameter that is a pointer to an object of the class type. This implicit parameter is named this, and is bound to the object on which the member function is called. Member functions may not define the this parameter; the compiler does so implicitly. The body of a member function may explicitly use the this pointer, but is not required to do so. The compiler treats an unqualified reference to a class member as if it had been made through the this pointer. 在第 7.7.1 节中已经提到,成员函数具有一个附加的隐含形参,即指向该类对象的一个指针。这个隐含形参命名为 this,与调用成员函数的对象绑定在一起。成员函数不能定义 this 形参,而是由编译器隐含地定义。成员函数的函数体可以显式使用 this 指针,但不是必须这么做。如果对类成员的引用没有限定,编译器会将这种引用处理成通过 this 指针的引用。 When to Use the this Pointer何时使用 this 指针Although it is usually unnecessary to refer explicitly to this inside a member function, there is one case in which we must do so: when we need to refer to the object as a whole rather than to a member of the object. The most common case where we must use this is in functions that return a reference to the object on which they were invoked. 尽管在成员函数内部显式引用 this 通常是不必要的,但有一种情况下必须这样做:当我们需要将一个对象作为整体引用而不是引用对象的一个成员时。最常见的情况是在这样的函数中使用 this:该函数返回对调用该函数的对象的引用。 The Screen class is a good example of the kind of class that might have operations that should return references. So far our class has only a pair of get operations. We might logically add: 某种类可能具有某些操作,这些操作应该返回引用,Screen 类就是这样的一个类。迄今为止,我们的类只有一对 get 操作。逻辑上,我们可以添加下面的操作。
Ideally, we'd like users to be able to concatenate a sequence of these actions into a single expression: 理想情况下,希望用户能够将这些操作的序列连接成一个单独的表达式:
// move cursor to given position, and set that character
myScreen.move(4,0).set('#');
We'd like this statement to be equivalent to 这个语句等价于: myScreen.move(4,0); myScreen.set('#'); Returning *this返回 *thisTo allow us to call move and set in a single expression, each of our new operations must return a reference to the object on which it executes: 在单个表达式中调用 move 和 set 操作时,每个操作必须返回一个引用,该引用指向执行操作的那个对象: class Screen { public: // interface member functions Screen& move(index r, index c); Screen& set(char); Screen& set(index, index, char); // other members as before }; Notice that the return type of these functions is Screen&, which indicates that the member function returns a reference to an object of its own class type. Each of these functions returns the object on which it was invoked. We'll use the this pointer to get access to the object. Here is the implementation for two of our new members: 注意,这些函数的返回类型是 Screen&,指明该成员函数返回对其自身类类型的对象的引用。每个函数都返回调用自己的那个对象。使用 this 指针来访问该对象。下面是对两个新成员的实现:
Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
Screen& Screen::move(index r, index c)
{
index row = r * width; // row location
cursor = row + c;
return *this;
}
The only interesting part in this function is the return statement. In each case, the function returns *this. In these functions, this is a pointer to a nonconst Screen. As with any pointer, we can access the object to which this points by dereferencing the this pointer. 函数中唯一需要关注的部分是 return 语句。在这两个操作中,每个函数都返回 *this。在这些函数中,this 是一个指向非常量 Screen 的指针。如同任意的指针一样,可以通过对 this 指针解引用来访问 this 指向的对象。 Returning *this from a const Member Function从 const 成员函数返回 *thisIn an ordinary nonconst member function, the type of this is a const pointer (Section 4.2.5, p. 126) to the class type. We may change the value to which this points but cannot change the address that this holds. In a const member function, the type of this is a const pointer to a const class-type object. We may change neither the object to which this points nor the address that this holds. 在普通的非 const 成员函数中,this 的类型是一个指向类类型的 const 指针(第 4.2.5 节)。可以改变 this 所指向的值,但不能改变 this 所保存的地址。在 const 成员函数中,this 的类型是一个指向 const 类类型对象的 const 指针。既不能改变 this 所指向的对象,也不能改变 this 所保存的地址。
As an example, we might add a display operation to our Screen class. This function should print contents on a given ostream. Logically, this operation should be a const member. Printing the contents doesn't change the object. If we make display a const member of Screen, then the this pointer inside display will be a const Screen* const. 例如,我们可以给 Screen 类增加一个 display 操作。这个函数应该在给定的 ostream 上打印 contents。逻辑上,这个操作应该是一个 const 成员。打印 contents 不会改变对象。如果将 display 作为 Screen 的 const 成员,则 display 内部的 this 指针将是一个 const Screen* 型的 const。 However, as we can with the move and set operations, we'd like to be able to use the display in a series of actions: 然而,与 move 和 set 操作一样,我们希望能够在一个操作序列中使用 display:
// move cursor to given position, set that character and display the screen
myScreen.move(4,0).set('#').display(cout);
This usage implies that display should return a Screen reference and take a reference to an ostream. If display is a const member, then its return type must be const Screen&. 这个用法暗示了 display 应该返回一个 Screen 引用,并接受一个 ostream 引用。如果 display 是一个 const 成员,则它的返回类型必须是 const Screen&。 Unfortunately, there is a problem with this design. If we define display as a const member, then we could call display on a nonconst object but would not be able to embed a call to display in a larger expression. The following code would be illegal: 不幸的是,这个设计存在一个问题。如果将 display 定义为 const 成员,就可以在非 const 对象上调用 display,但不能将对 display 的调用嵌入到一个长表达式中。下面的代码将是非法的: Screen myScreen; // this code fails if display is a const member function // display return a const reference; we cannot call set on a const myScreen.display().set('*'); The problem is that this expression runs set on the object returned from display. That object is const because display returns its object as a const. We cannot call set on a const object. 问题在于这个表达式是在由 display 返回的对象上运行 set。该对象是 const,因为 display 将其对象作为 const 返回。我们不能在 const 对象上调用 set。 Overloading Based on const基于 const 的重载To solve this problem we must define two display operations: one that is const and one that isn't. We can overload a member function based on whether it is const for the same reasons that we can overload a function based on whether a pointer parameter points to const (Section 7.8.4, p. 275). A const object will use only the const member. A nonconst object could use either member, but the nonconst version is a better match. 为了解决这个问题,我们必须定义两个 display 操作:一个是 const,另一个不是 const。基于成员函数是否为 const,可以重载一个成员函数;同样地,基于一个指针形参是否指向 const(第 7.8.4 节),可以重载一个函数。const 对象只能使用 const 成员。非 const 对象可以使用任一成员,但非 const 版本是一个更好的匹配。 While we're at it, we'll define a private member named do_display to do the actual work of printing the Screen. Each of the display operations will call this function and then return the object on which it is executing: 在此,我们将定义一个名为 do_display 的 private 成员来打印 Screen。每个 display 操作都将调用此函数,然后返回调用自己的那个对象: class Screen { public: // interface member functions // display overloaded on whether the object is const or not Screen& display(std::ostream &os) { do_display(os); return *this; } const Screen& display(std::ostream &os) const { do_display(os); return *this; } private: // single function to do the work of displaying a Screen, // will be called by the display operations void do_display(std::ostream &os) const { os << contents; } // as before }; Now, when we embed display in a larger expression, the nonconst version will be called. When we display a const object, then the const version is called: 现在,当我们将 display 嵌入到一个长表达式中时,将调用非 const 版本。当我们 display 一个 const 对象时,就调用 const 版本: Screen myScreen(5,3); const Screen blank(5, 3); myScreen.set('#').display(cout); // calls nonconst version blank.display(cout); // calls const version Mutable Data Members可变数据成员It sometimes (but not very often) happens that a class has a data member that we want to be able to modify, even inside a const member function. We can indicate such members by declaring them as mutable. 有时(但不是很经常),我们希望类的数据成员(甚至在 const 成员函数内)可以修改。这可以通过将它们声明为 mutable 来实现。 A mutable data member is a member that is never const, even when it is a member of a const object. Accordingly, a const member function may change a mutable member. To declare a data member as mutable, the keyword mutable must precede the declaration of the member: 可变数据成员(mutable data member)永远都不能为 const,甚至当它是 const 对象的成员时也如此。因此,const 成员函数可以改变 mutable 成员。要将数据成员声明为可变的,必须将关键字 mutable 放在成员声明之前: class Screen { public: // interface member functions private: mutable size_t access_ctr; // may change in a const members // other data members as before }; We've given Screen a new data member named access_ctr that is mutable. We'll use access_ctr to track how often Screen member functions are called: 我们给 Screen 添加了一个新的可变数据成员 access_ctr。使用 access_ctr 来跟踪调用 Screen 成员函数的频繁程度:
void Screen::do_display(std::ostream& os) const
{
++access_ctr; // keep count of calls to any member function
os << contents;
}
Even though do_display is const, it can increment access_ctr. That member is a mutable member, so any member function, including const functions, can change the value of access_ctr. 尽管 do_display 是 const,它也可以增加 access_ctr。该成员是可变成员,所以,任意成员函数,包括 const 函数,都可以改变 access_ctr 的值。
![]() |