Using the this Pointer
We have seen that an object's member functions can manipulate the object's data. 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). An object's this pointer is not part of the object itselfi.e., the size of 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 10.7 introduces static class members and explains why the this pointer is not implicitly passed to static member functions.
Objects use the this pointer implicitly (as we have done to this point) or explicitly to reference their data members and member functions. 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. For example, in a nonconstant member function of class Employee, the this pointer has type Employee * const (a constant pointer to a nonconstant Employee object). In a constant member function of the class Employee, the this pointer has the data type const Employee * const (a constant pointer to a constant Employee object).
Our first example in this section shows implicit and explicit use of the this pointer; later in this chapter and in Chapter 11, we show some substantial and subtle examples of using this.
Implicitly and Explicitly Using the this Pointer to Access an Object's Data Members
Figure 10.17 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.
Figure 10.17. this pointer implicitly and explicitly accessing an object's members.
(This item is displayed on pages 545 - 546 in the print version)
1 // Fig. 10.17: fig10_17.cpp 2 // Using the this pointer to refer to object members. 3 #include 4 using std::cout; 5 using std::endl; 6 7 class Test 8 { 9 public: 10 Test( int = 0 ); // default constructor 11 void print() const; 12 private: 13 int x; 14 }; // end class Test 15 16 // constructor 17 Test::Test( int value ) 18 : x( value ) // initialize x to value 19 { 20 // empty body 21 } // end constructor Test 22 23 // print x using implicit and explicit this pointers; 24 // the parentheses around *this are required 25 void Test::print() const 26 { 27 // implicitly use the this pointer to access the member x 28 cout << " x = " << x; 29 30 // explicitly use the this pointer and the arrow operator 31 // to access the member x 32 cout << " this->x = " << this->x; 33 34 // explicitly use the dereferenced this pointer and 35 // the dot operator to access the member x 36 cout << " (*this).x = " << ( *this ).x << endl; 37 } // end function print 38 39 int main() 40 { 41 Test testObject( 12 ); // instantiate and initialize testObject 42 43 testObject.print(); 44 return 0; 45 } // end main
|
For illustration purposes, member function print (lines 2537) first prints x by using the this pointer implicitly (line 28)only the name of the data member is specified. Then print uses two different notations to access x tHRough the this pointerthe arrow operator (->) off the this pointer (line 32) and the dot operator (.) off the dereferenced this pointer (line 36).
Note the parentheses around *this (line 36) 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 will see in Chapter 11, self-assignment can cause serious errors when the object contains pointers to dynamically allocated storage.
Common Programming Error 10.7
Attempting to use the member selection operator (.) with a pointer to an object is a compilation errorthe dot member selection operator may be used only with an lvalue such as an object's name, a reference to an object or a dereferenced pointer to an object. |
Using the this Pointer to Enable Cascaded Function Calls
Another use of the this pointer is to enable cascaded member-function calls in which multiple functions are invoked in the same statement (as in line 14 of Fig. 10.20). The program of Figs. 10.1810.20 modifies class Time's set functions setTime, setHour, setMinute and setSecond such that each returns a reference to a Time object to enable cascaded member-function calls. Notice in Fig. 10.19 that the last statement in the body of each of these member functions returns *this (lines 26, 33, 40 and 47) into a return type of Time &.
Figure 10.18. Time class definition modified to enable cascaded member-function calls.
(This item is displayed on pages 547 - 548 in the print version)
1 // Fig. 10.18: Time.h 2 // Cascading member function calls. 3 4 // Time class definition. 5 // Member functions defined in Time.cpp. 6 #ifndef TIME_H 7 #define TIME_H 8 9 class Time 10 { 11 public: 12 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 // get functions (normally declared const) 21 int getHour() const; // return hour 22 int getMinute() const; // return minute 23 int getSecond() const; // return second 24 25 // print functions (normally declared const) 26 void printUniversal() const; // print universal time 27 void printStandard() const; // print standard time 28 private: 29 int hour; // 0 - 23 (24-hour clock format) 30 int minute; // 0 - 59 31 int second; // 0 - 59 32 }; // end class Time 33 34 #endif |
Figure 10.19. Time class member-function definitions modified to enable cascaded member-function calls.
(This item is displayed on pages 548 - 549 in the print version)
1 // Fig. 10.19: Time.cpp 2 // Member-function definitions for Time class. 3 #include 4 using std::cout; 5 6 #include 7 using std::setfill; 8 using std::setw; 9 10 #include "Time.h" // Time class definition 11 12 // constructor function to initialize private data; 13 // calls member function setTime to set variables; 14 // default values are 0 (see class definition) 15 Time::Time( int hr, int min, int sec ) 16 { 17 setTime( hr, min, sec ); 18 } // end Time constructor 19 20 // set values of hour, minute, and second 21 Time &Time::setTime( int h, int m, int s ) // note Time & return 22 { 23 setHour( h ); 24 setMinute( m ); 25 setSecond( s ); 26 return *this; // enables cascading 27 } // end function setTime 28 29 // set hour value 30 Time &Time::setHour( int h ) // note Time & return 31 { 32 hour = ( h >= 0 && h < 24 ) ? h : 0; // validate hour 33 return *this; // enables cascading 34 } // end function setHour 35 36 // set minute value 37 Time &Time::setMinute( int m ) // note Time & return 38 { 39 minute = ( m >= 0 && m < 60 ) ? m : 0; // validate minute 40 return *this; // enables cascading 41 } // end function setMinute 42 43 // set second value 44 Time &Time::setSecond( int s ) // note Time & return 45 { 46 second = ( s >= 0 && s < 60 ) ? s : 0; // validate second 47 return *this; // enables cascading 48 } // end function setSecond 49 50 // get hour value 51 int Time::getHour() const 52 { 53 return hour; 54 } // end function getHour 55 56 // get minute value 57 int Time::getMinute() const 58 { 59 return minute; 60 } // end function getMinute 61 62 // get second value 63 int Time::getSecond() const 64 { 65 return second; 66 } // end function getSecond 67 68 // print Time in universal-time format (HH:MM:SS) 69 void Time::printUniversal() const 70 { 71 cout << setfill( '0' ) << setw( 2 ) << hour << ":" 72 << setw( 2 ) << minute << ":" << setw( 2 ) << second; 73 } // end function printUniversal 74 75 // print Time in standard-time format (HH:MM:SS AM or PM) 76 void Time::printStandard() const 77 { 78 cout << ( ( hour == 0 || hour == 12 ) ? 12 : hour % 12 ) 79 << ":" << setfill( '0' ) << setw( 2 ) << minute 80 << ":" << setw( 2 ) << second << ( hour < 12 ? " AM" : " PM" ); 81 } // end function printStandard |
Figure 10.20. Cascading member-function calls.
(This item is displayed on page 550 in the print version)
1 // Fig. 10.20: fig10_20.cpp 2 // Cascading member function calls with the this pointer. 3 #include 4 using std::cout; 5 using std::endl; 6 7 #include "Time.h" // Time class definition 8 9 int main() 10 { 11 Time t; // create Time object 12 13 // cascaded function calls 14 t.setHour( 18 ).setMinute( 30 ).setSecond( 22 ); 15 16 // output time in universal and standard formats 17 cout << "Universal time: "; 18 t.printUniversal(); 19 20 cout << " Standard time: "; 21 t.printStandard(); 22 23 cout << " New standard time: "; 24 25 // cascaded function calls 26 t.setTime( 20, 20, 20 ).printStandard(); 27 cout << endl; 28 return 0; 29 } // end main
|
The program of Fig. 10.20 creates Time object t (line 11), then uses it in cascaded member-function calls (lines 14 and 26). Why does the technique of returning *this as a reference work? The dot operator (.) associates from left to right, so line 14 first evaluates t.setHour( 18 ) then returns a reference to 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 object t. The remaining expression is interpreted as
t.setSecond( 22 );
Line 26 also uses cascading. The calls must appear in the order shown in line 26, because printStandard as defined in the class does not return a reference to t. Placing the call to printStandard before the call to setTime in line 26 results in a compilation error. Chapter 11 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.