Time Class Case Study: A Subtle TrapReturning a Reference to a private Data Member

Time Class Case Study A Subtle TrapReturning a Reference to a private Data Member

A reference to an object is an alias for the name of the object and, hence, may be used on the left side of an assignment statement. In this context, the reference makes a perfectly acceptable lvalue that can receive a value. One way to use this capability (unfortunately!) is to have a public member function of a class return a reference to a private data member of that class. Note that if a function returns a const reference, that reference cannot be used as a modifiable lvalue.

The program of Figs. 9.149.16 uses a simplified Time class (Fig. 9.14 and Fig. 9.15) to demonstrate returning a reference to a private data member with member function badSetHour (declared in Fig. 9.14 at line 15 and defined in Fig. 9.15 at lines 2933). Such a reference return actually makes a call to member function badSetHour an alias for private data member hour! The function call can be used in any way that the private data member can be used, including as an lvalue in an assignment statement, thus enabling clients of the class to clobber the class's private data at will! Note that the same problem would occur if a pointer to the private data were to be returned by the function.


Figure 9.14. Returning a reference to a private data member.

 1 // Fig. 9.14: Time.h
 2 // Declaration of class Time.
 3 // Member functions defined in Time.cpp
 4
 5 // prevent multiple inclusions of header file
 6 #ifndef TIME_H
 7 #define TIME_H
 8
 9 class Time
10 {
11 public:
12 Time( int = 0, int = 0, int = 0 );
13 void setTime( int, int, int );
14 int getHour();
15 int &badSetHour( int ); // DANGEROUS reference return
16 private:
17 int hour;
18 int minute;
19 int second;
20 }; // end class Time
21
22 #endif

Figure 9.15. Returning a reference to a private data member.

(This item is displayed on pages 504 - 505 in the print version)

 1 // Fig. 9.15: Time.cpp
 2 // Member-function definitions for Time class.
 3 #include "Time.h" // include definition of class Time
 4
 5 // constructor function to initialize private data;
 6 // calls member function setTime to set variables;
 7 // default values are 0 (see class definition)
 8 Time::Time( int hr, int min, int sec )
 9 {
10 setTime( hr, min, sec );
11 } // end Time constructor
12
13 // set values of hour, minute and second
14 void Time::setTime( int h, int m, int s )
15 {
16 hour = ( h >= 0 && h < 24 ) ? h : 0; // validate hour
17 minute = ( m >= 0 && m < 60 ) ? m : 0; // validate minute
18 second = ( s >= 0 && s < 60 ) ? s : 0; // validate second
19 } // end function setTime
20
21 // return hour value
22 int Time::getHour()
23 {
24 return hour;
25 } // end function getHour
26
27 // POOR PROGRAMMING PRACTICE: 
28 // Returning a reference to a private data member.
29 int &Time::badSetHour( int hh ) 
30 { 
31  hour = ( hh >= 0 && hh < 24 ) ? hh : 0; 
32  return hour; // DANGEROUS reference return 
33 } // end function badSetHour 

Figure 9.16 declares Time object t (line 12) and reference hourRef (line 15), which is initialized with the reference returned by the call t.badSetHour(20). Line 17 displays the value of the alias hourRef. This shows how hourRef breaks the encapsulation of the classstatements in main should not have access to the private data of the class. Next, line 18 uses the alias to set the value of hour to 30 (an invalid value) and line 19 displays the value returned by function getHour to show that assigning a value to hourRef actually modifies the private data in the Time object t. Finally, line 23 uses the badSetHour function call itself as an lvalue and assigns 74 (another invalid value) to the reference returned by the function. Line 28 again displays the value returned by function getHour to show that assigning a value to the result of the function call in line 23 modifies the private data in the Time object t.


Figure 9.16. Returning a reference to a private data member.

(This item is displayed on pages 505 - 506 in the print version)

 1 // Fig. 9.16: fig09_16.cpp
 2 // Demonstrating a public member function that
 3 // returns a reference to a private data member.
 4 #include 
 5 using std::cout;
 6 using std::endl;
 7
 8 #include "Time.h" // include definition of class Time
 9
10 int main()
11 {
12 Time t; // create Time object
13
14 // initialize hourRef with the reference returned by badSetHour
15 int &hourRef = t.badSetHour( 20 ); // 20 is a valid hour 
16
17 cout << "Valid hour before modification: " << hourRef; 
18 hourRef = 30; // use hourRef to set invalid value in Time object t
19 cout << "
Invalid hour after modification: " << t.getHour();
20
21 // Dangerous: Function call that returns 
22 // a reference can be used as an lvalue! 
23 t.badSetHour( 12 ) = 74; // assign another invalid value to hour
24
25 cout << "

*************************************************
"
26 << "POOR PROGRAMMING PRACTICE!!!!!!!!
"
27 << "t.badSetHour( 12 ) as an lvalue, invalid hour: "
28 << t.getHour()
29 << "
*************************************************" << endl;
30 return 0;
31 } // end main
 
 Valid hour before modification: 20
 Invalid hour after modification: 30

 *************************************************
 POOR PROGRAMMING PRACTICE!!!!!!!!
 t.badSetHour( 12 ) as an lvalue, invalid hour: 74
 *************************************************
 

Error-Prevention Tip 9.4

Returning a reference or a pointer to a private data member breaks the encapsulation of the class and makes the client code dependent on the representation of the class's data. So, returning pointers or references to private data is a dangerous practice that should be avoided.






C++ How to Program
C++ How to Program (5th Edition)
ISBN: 0131857576
EAN: 2147483647
Year: 2004
Pages: 627
Simiral book on Amazon

Flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net