9.14 Using the this Pointer

There’s only one copy of each class’s functionality, but there can be many objects of a class, so how do member functions know which object’s data members to manipulate? Every object has access to its own address through a pointer called this (a C++ keyword). The this pointer is not part of the object itself—i.e., the memory occupied by the this pointer is not reflected in the result of a sizeof operation on the object. Rather, the this pointer is passed (by the compiler) as an implicit argument to each of the object’s non-static member functions. Section 9.15 introduces static class members and explains why the this pointer is not implicitly passed to static member functions.

Using the this Pointer to Avoid Naming Collisions

Member functions use the this pointer implicitly (as we’ve done so far) or explicitly to reference an object’s data members and other member functions. A common explicit use of the this pointer is to avoid naming conflicts between a class’s data members and member-function parameters (or other local variables). If a member function contains a local variable and data member with the same name, as in the following setHour function:


// set hour value
void Time::setHour(int hour) {
   if (hour >= 0 && hour < 24) {
      this->hour = hour; // use this-> to access data member
   }
   else {
      throw invalid_argument("hour must be 0-23");
   }
}

the local variable is said to hide or shadow the data member—using just the variable name in the member function’s body refers to the local variable rather than the data member. However, you can access the data member hour by qualifying its name with this->. So the following statement assigns the hour parameter’s value to the data member hour


this->hour = hour; // use this-> to access data member

Good Programming Practice 9.4

A widely accepted practice to minimize the proliferation of identifier names is to use the same name for a set function’s parameter and the data member it sets, and to reference the data member in the set function’s body via this->.

Type of the this Pointer

The type of the this pointer depends on the type of the object and whether the member function in which this is used is declared const:

  • In a non-const member function of class Employee, the this pointer has the type Employee* const—a constant pointer to a nonconstant Employee.

  • In a const member function, this has the type const Employee* const—a constant pointer to a constant Employee.

9.14.1 Implicitly and Explicitly Using the this Pointer to Access an Object’s Data Members

Figure 9.24 demonstrates the implicit and explicit use of the this pointer to enable a member function of class Test to print the private data x of a Test object. In the next example and in Chapter 10, we show some substantial and subtle examples of using this.

Fig. 9.24 Using the this pointer to refer to object members.

Alternate View

 1   // Fig. 9.24: fig09_24.cpp
 2   // Using the this pointer to refer to object members.
 3   #include <iostream>
 4   using namespace std;
 5
 6   class Test {
 7   public:
 8      explicit Test(int);
 9      void print() const;
10   private:
11      int x{0};
12   };
13
14   // constructor
15   Test::Test(int value) : x{value} {} // initialize x to value
16
17   // print x using implicit then explicit this pointers;
18   // the parentheses around *this are required
19   void Test::print() const {
20      // implicitly use the this pointer to access the member x
21      cout << "        x = " << x;                             
22
23      // explicitly use the this pointer and the arrow operator
24      // to access the member x                                
25      cout << "
 this->x = "  << this->x;                     
26
27      // explicitly use the dereferenced this pointer and
28      // the dot operator to access the member x         
29      cout << "
(*this).x = " << (*this).x << endl;     
30   }
31
32   int main() {
33      Test testObject{12}; // instantiate and initialize testObject
34      testObject.print();
35   }

        x = 12
  this->x = 12
(*this).x = 12

For illustration purposes, member function print (lines 19–30) first prints x by using the this pointer implicitly (line 21)—only the name of the data member is specified. Then print uses two different notations to access x through the this pointer—the arrow operator (->) off the this pointer (line 25) and the dot operator (.) off the dereferenced this pointer (line 29). Note the parentheses around *this (line 29) when used with the dot member-selection operator (.). The parentheses are required because the dot operator has higher precedence than the * operator. Without the parentheses, the expression *this.x would be evaluated as if it were parenthesized as *(this.x), which is a compilation error, because the dot operator cannot be used with a pointer.

One interesting use of the this pointer is to prevent an object from being assigned to itself. As we’ll see in Chapter 10, self-assignment can cause serious errors when the object contains pointers to dynamically allocated storage.

9.14.2 Using the this Pointer to Enable Cascaded Function Calls

Another use of the this pointer is to enable cascaded member-function calls—that is, invoking multiple functions sequentially in the same statement (as in line 10 of Fig. 9.27). The program of Figs. 9.259.27 modifies class Time’s set functions setTime, setHour, setMinute and setSecond such that each returns a reference to the Time object on which it’s called to enable cascaded member-function calls. Notice in Fig. 9.26 that the last statement in the body of each of these member functions returns *this (lines 21, 33, 45 and 57) into a return type of Time&.

Fig. 9.25 Time class modified to enable cascaded member-function calls.

Alternate View

 1   // Fig. 9.25: Time.h
 2   // Time class modified to enable cascaded member-function calls.
 3   #include <string>
 4
 5   // Time class definition.
 6   // Member functions defined in Time.cpp.
 7   #ifndef TIME_H
 8   #define TIME_H
 9
10   class Time {
11   public:
12      explicit Time(int = 0, int = 0, int = 0); // default constructor
13
14       // set functions (the Time& return types enable cascading)
15       Time& setTime(int, int, int); // set hour, minute, second 
16       Time& setHour(int); // set hour                           
17       Time& setMinute(int); // set minute                       
18       Time& setSecond(int); // set second                       
19
20       unsigned int getHour() const; // return hour
21       unsigned int getMinute() const; // return minute
22       unsigned int getSecond() const; // return second
23       std::string toUniversalString() const; // 24-hour time format string
24       std::string toStandardString() const; // 12-hour time format string
25    private:
26       unsigned int hour{0}; // 0 - 23 (24-hour clock format)
27       unsigned int minute{0}; // 0 - 59
28       unsigned int second{0}; // 0 - 59
29    };
30
31    #endif

Fig. 9.26 Time class member-function definitions modified to enable cascaded member-function calls.

Alternate View

 1   // Fig. 9.26: Time.cpp
 2   // Time class member-function definitions.
 3   #include <iomanip>
 4   #include <sstream>
 5   #include <stdexcept>
 6   #include "Time.h" // Time class definition
 7   using namespace std;
 8
 9   // constructor function to initialize private data;
10   // calls member function setTime to set variables;
11   // default values are 0 (see class definition)
12   Time::Time(int hr, int min, int sec) {
13      setTime(hr, min, sec);
14   }
15
16   // set values of hour, minute, and second
17   Time& Time::setTime(int h, int m, int s) { // note Time& return
18      setHour(h);
19      setMinute(m);
20      setSecond(s);
21      return *this; // enables cascading
22   }
23
24   // set hour value
25   Time& Time::setHour(int h) { // note Time& return
26   if (h >= 0 && h < 24) {
27      hour = h;
28   }
29   else {
30      throw invalid_argument("hour must be 0-23");
31   }
32
33   return *this; // enables cascading
34  }
35
36  // set minute value
37  Time& Time::setMinute(int m) { // note Time& return
38     if (m >= 0 && m < 60) {
39        minute = m;
40     }
41     else {
42        throw invalid_argument("minute must be 0-59");
43     }
44
45     return *this; // enables cascading
46  }
47
48  // set second value
49  Time& Time::setSecond(int s) { // note Time& return
50     if (s >= 0 && s < 60) {
51        second = s;
52     }
53     else {
54        throw invalid_argument("second must be 0-59");
55     }
56
57     return *this; // enables cascading
58  }
59
60  // get hour value
61  unsigned int Time::getHour() const {return hour;}
62
63  // get minute value
64  unsigned int Time::getMinute() const {return minute;}
65
66  // get second value
67  unsigned int Time::getSecond() const {return second;}
68
69  // return Time as a string in universal-time format (HH:MM:SS)
70  string Time::toUniversalString() const {
71    ostringstream output;
72    output << setfill('0') << setw(2) << getHour() << ":"
73       << setw(2) << getMinute() << ":" << setw(2) << getSecond();
74    return output.str();
75  }
76
77  // return Time as string in standard-time format (HH:MM:SS AM or PM)
78  string Time::toStandardString() const {
79     ostringstream output;
80     output << ((getHour() == 0 || getHour() == 12) ? 12 : getHour() % 12)
81        << ":" << setfill('0') << setw(2) << getMinute() << ":" << setw(2)
82        << getSecond() << (hour < 12 ? " AM" : " PM");
83     return output.str();
84   }

The program of Fig. 9.27 creates Time object t (line 8), then uses it in cascaded member-function calls (lines 10 and 18). Why does the technique of returning *this as a reference work? The dot operator (.) associates from left to right, so line 10


t.setHour(18).setMinute(30).setSecond(22);

first evaluates t.setHour(18), then returns a reference to (the updated) object t as the value of this function call. The remaining expression is then interpreted as


t.setMinute(30).setSecond(22);

The t.setMinute(30) call executes and returns a reference to the (further updated) object t. The remaining expression is interpreted as


t.setSecond(22);

Fig. 9.27 Cascading member-function calls with the this pointer.

Alternate View

 1   // Fig. 9.27: fig09_27.cpp
 2   // Cascading member-function calls with the this pointer.
 3   #include <iostream>
 4   #include "Time.h" // Time class definition
 5   using namespace std;
 6
 7   int main() {
 8      Time t; // create Time object
 9
10      t.setHour(18).setMinute(30).setSecond(22); // cascaded function calls
11
12      // output time in universal and standard formats
13      cout << "Universal time: " << t.toUniversalString()
14         << "
Standard time: " << t.toStandardString();
15
16     // cascaded function calls
17     cout << "

New standard time: "
18        << t.setTime(20, 20, 20).toStandardString() << endl;
19   }

Universal time: 18:30:22
Standard time: 6:30:22 PM

New standard time: 8:20:20 PM

Line 18 (Fig. 9.27) also uses cascading. Note that we cannot chain another Time member-function call after toStandardString here, because it does not return a reference to t—we could, however, place a call to a string member function, because toStandardString returns a string. Placing the call to toStandardString before the call to setTime in line 18 results in a compilation error, because the string returned by toStandardString does not have a setTime function. Chapter 10 presents several practical examples of using cascaded function calls. One such example uses multiple << operators with cout to output multiple values in a single statement.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset