9.8. Time Class Case Study: A Subtle Trap—Returning a Reference or a Pointer 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 is to have a public member function of a class return a reference to a private data member of that class. If a function returns a reference that is declared const, the reference is a non-modifiable lvalue and cannot be used to modify the data.

The program of Figs. 9.109.12 uses a simplified Time class (Fig. 9.10 and Fig. 9.11) to demonstrate returning a reference to a private data member with member function badSetHour (declared in Fig. 9.10 in line 15 and defined in Fig. 9.11 in lines 37–45). 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! A similar problem would occur if a pointer to the private data were to be returned by the function.


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


Fig. 9.10. Time class declaration.


 1   // Fig. 9.11: Time.cpp
 2   // Time class member-function definitions.
 3   #include <stdexcept>
 4   #include "Time.h" // include definition of class Time
 5   using namespace std;
 6
 7   // constructor function to initialize private data; calls member function
 8   // setTime to set variables; default values are 0 (see class definition)
 9   Time::Time( int hr, int min, int sec )
10   {
11      setTime( hr, min, sec );
12   } // end Time constructor
13
14   // set values of hour, minute and second
15   void Time::setTime( int h, int m, int s )
16   {
17      // validate hour, minute and second
18      if ( ( h >= 0 && h < 24 ) && ( m >= 0 && m < 60 ) &&
19         ( s >= 0 && s < 60 ) )
20      {
21         hour = h;
22         minute = m;
23         second = s;
24      } // end if
25      else
26         throw invalid_argument(
27            "hour, minute and/or second was out of range" );
28   } // end function setTime
29
30   // return hour value
31   unsigned int Time::getHour()
32   {
33      return hour;
34   } // end function getHour
35
36   // poor practice: returning a reference to a private data member.
37   unsigned int &Time::badSetHour( int hh )                         
38   {
39      if ( hh >= 0 && hh < 24 )
40         hour = hh;
41      else
42         throw invalid_argument( "hour must be 0-23" );
43
44      return hour; // dangerous reference return
45   } // end function badSetHour


Fig. 9.11. Time class member-function definitions.


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


Valid hour before modification: 20
Invalid hour after modification: 30

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


Fig. 9.12. public member function that returns a reference to a private data member.

Figure 9.12 declares Time object t (line 10) and reference hourRef (line 13), which is initialized with the reference returned by the call t.badSetHour(20). Line 15 displays the value of the alias hourRef. This shows how hourRef breaks the encapsulation of the class—statements in main should not have access to the private data of the class. Next, line 16 uses the alias to set the value of hour to 30 (an invalid value) and line 17 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 21 uses the badSetHour function call itself as an lvalue and assigns 74 (another invalid value) to the reference returned by the function. Line 26 again displays the value returned by function getHour to show that assigning a value to the result of the function call in line 21 modifies the private data in the Time object t.


Image Software Engineering Observation 9.7

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. There are cases where doing this is appropriate—we’ll show an example of this when we build our custom Array class in Section 10.10.


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

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