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.10–9.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
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
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
*************************************************
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
.
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.
18.116.67.212