1 of 47

Operator Overloading

2 of 47

Introduction�

  • Operator overloading
    • Enabling C++’s operators to work with class objects
    • Using traditional operators with user-defined objects
    • Requires great care; when overloading is misused, program difficult to understand
    • Examples of already overloaded operators
      • Operator << is both the stream-insertion operator and the bitwise left-shift operator
      • + and -, perform arithmetic on multiple types
    • Compiler generates the appropriate code based on the manner in which the operator is used

​

3 of 47

Introduction

  • Overloading an operator
    • Write function definition as normal
    • Function name is keyword operator followed by the symbol for the operator being overloaded
    • operator+ used to overload the addition operator (+)
  • Using operators
    • To use an operator on a class object it must be overloaded unless the assignment operator(=)or the address operator(&)
      • Assignment operator by default performs memberwise assignment
      • Address operator (&) by default returns the address of an object

4 of 47

Restrictions on Operator Overloading �

  • C++ operators that can be overloaded

​

​

​

​

  • C++ Operators that cannot be overloaded

5 of 47

Restrictions on Operator Overloading

  • Overloading restrictions
    • Precedence of an operator cannot be changed
    • Associativity of an operator cannot be changed
    • Arity (number of operands) cannot be changed
      • Unary operators remain unary, and binary operators remain binary
      • Operators &, *, + and - each have unary and binary versions
      • Unary and binary versions can be overloaded separately
  • No new operators can be created
    • Use only existing operators
  • No overloading operators for built-in types
    • Cannot change how two integers are added
    • Produces a syntax error

6 of 47

Operator Functions as Class Members vs. as friend Functions

  • Member vs non-member
    • Operator functions can be member or non-member functions
    • When overloading ( ), [ ], -> or any of the assignment operators, must use a member function
  • Operator functions as member functions
    • Leftmost operand must be an object (or reference to an object) of the class
      • If left operand of a different type, operator function must be a non-member function
  • Operator functions as non-member functions
    • Must be friends if needs to access private or protected members
    • Enable the operator to be commutative

7 of 47

Overloading Stream-Insertion and Stream-Extraction Operators

  • Overloaded << and >> operators
    • Overloaded to perform input/output for user-defined types
    • Left operand of types ostream & and istream &
    • Must be a non-member function because left operand is not an object of the class
    • Must be a friend function to access private data members

8 of 47

1 // Fig. 8.3: fig08_03.cpp

​

2 // Overloading the stream-insertion and

​

3 // stream-extraction operators.

​

4 #include <iostream>

​

5

​

6 using std::cout;

​

7 using std::cin;

​

8 using std::endl;

​

9 using std::ostream;

​

10 using std::istream;

​

11

​

12 #include <iomanip>

​

13

​

14 using std::setw;

​

15

​

16 class PhoneNumber {

​

17 friend ostream &operator<<( ostream&, const PhoneNumber & );

​

18 friend istream &operator>>( istream&, PhoneNumber & );

​

19

​

20 private:

​

21 char areaCode[ 4 ]; // 3-digit area code and null

​

22 char exchange[ 4 ]; // 3-digit exchange and null

​

23 char line[ 5 ]; // 4-digit line and null

​

24 };

​

25

​

26 // Overloaded stream-insertion operator (cannot be

​

27 // a member function if we would like to invoke it with

​

28 // cout << somePhoneNumber;).

​

29 ostream &operator<<( ostream &output, const PhoneNumber &num )

​

30 {

​

9 of 47

31 output << "(" << num.areaCode << ") "

​

32 << num.exchange << "-" << num.line;

​

33 return output; // enables cout << a << b << c;

​

34 }

​

35

​

36 istream &operator>>( istream &input, PhoneNumber &num )

​

37 {

​

38 input.ignore(); // skip (

​

39 input >> setw( 4 ) >> num.areaCode; // input area code

​

40 input.ignore( 2 ); // skip ) and space

​

41 input >> setw( 4 ) >> num.exchange; // input exchange

​

42 input.ignore(); // skip dash (-)

​

43 input >> setw( 5 ) >> num.line; // input line

​

44 return input; // enables cin >> a >> b >> c;

​

45 }

​

46

​

47 int main()

​

48 {

​

49 PhoneNumber phone; // create object phone

​

50

​

51 cout << "Enter phone number in the form (123) 456-7890:\n";

​

52

​

53 // cin >> phone invokes operator>> function by

​

54 // issuing the call operator>>( cin, phone ).

​

55 cin >> phone;

​

56

​

57 // cout << phone invokes operator<< function by

​

58 // issuing the call operator<<( cout, phone ).

​

59 cout << "The phone number entered was: " << phone << endl;

​

60 return 0;

​

61 }

​

10 of 47

Program Output

​

Enter phone number in the form (123) 456-7890:

(800) 555-1212

The phone number entered was: (800) 555-1212

​

11 of 47

Overloading Unary Operators�

  • Overloading unary operators
    • Can be overloaded with no arguments or one argument
    • Should usually be implemented as member functions
      • Avoid friend functions and classes because they violate the encapsulation of a class
    • Example declaration as a member function:

class String {�public:� bool operator!() const;� ...�};

12 of 47

Overloading Unary Operators

    • Example declaration as a non-member function

class String {� friend bool operator!( const String & )� ...�}

​

13 of 47

Overloading Binary Operators

  • Overloaded Binary operators
    • Non-static member function, one argument
    • Example:

class String {

public:

const String &operator+=(

const String & );

...

};

    • y += z is equivalent to y.operator+=( z )

14 of 47

Overloading Binary Operators

    • Non-member function, two arguments
    • Example:

class String {

friend const String &operator+=(

String &, const String & );

...

};

    • y += z is equivalent to operator+=( y, z )

​

15 of 47

Case Study: An Array class�

  • Implement an Array class with
    • Range checking
    • Array assignment
    • Arrays that know their size
    • Outputting/inputting entire arrays with << and >>
    • Array comparisons with == and !=

​

16 of 47

1 // Fig. 8.4: array1.h

​

2 // Simple class Array (for integers)

​

3 #ifndef ARRAY1_H

​

4 #define ARRAY1_H

​

5

​

6 #include <iostream>

​

7

​

8 using std::ostream;

​

9 using std::istream;

​

10

​

11 class Array {

​

12 friend ostream &operator<<( ostream &, const Array & );

​

13 friend istream &operator>>( istream &, Array & );

​

14 public:

​

15 Array( int = 10 ); // default constructor

​

16 Array( const Array & ); // copy constructor

​

17 ~Array(); // destructor

​

18 int getSize() const; // return size

​

19 const Array &operator=( const Array & ); // assign arrays

​

20 bool operator==( const Array & ) const; // compare equal

​

21

​

22 // Determine if two arrays are not equal and

​

23 // return true, otherwise return false (uses operator==).

​

24 bool operator!=( const Array &right ) const

​

25 { return ! ( *this == right ); }

​

26

​

27 int &operator[]( int ); // subscript operator

​

28 const int &operator[]( int ) const; // subscript operator

​

29 static int getArrayCount(); // Return count of

​

30 // arrays instantiated.

​

31 private:

​

32 int size; // size of the array

​

33 int *ptr; // pointer to first element of array

​

34 static int arrayCount; // # of Arrays instantiated

​

17 of 47

 

 

​

35 };

​

36

​

37 #endif

​

38 // Fig 8.4: array1.cpp

​

39 // Member function definitions for class Array

​

40 #include <iostream>

​

41

​

42 using std::cout;

​

43 using std::cin;

​

44 using std::endl;

​

45

​

46 #include <iomanip>

​

47

​

48 using std::setw;

​

49

​

50 #include <cstdlib>

​

51 #include <cassert>

​

52 #include "array1.h"

​

53

​

54 // Initialize static data member at file scope

​

55 int Array::arrayCount = 0; // no objects yet

​

56

​

57 // Default constructor for class Array (default size 10)

​

58 Array::Array( int arraySize )

​

59 {

​

60 size = ( arraySize > 0 ? arraySize : 10 );

​

61 ptr = new int[ size ]; // create space for array

​

62 assert( ptr != 0 ); // terminate if memory not allocated

​

63 ++arrayCount; // count one more object

​

64

​

65 for ( int i = 0; i < size; i++ )

​

66 ptr[ i ] = 0; // initialize array

​

18 of 47

67 }

​

68

​

69 // Copy constructor for class Array

​

70 // must receive a reference to prevent infinite recursion

​

71 Array::Array( const Array &init ) : size( init.size )

​

72 {

​

73 ptr = new int[ size ]; // create space for array

​

74 assert( ptr != 0 ); // terminate if memory not allocated

​

75 ++arrayCount; // count one more object

​

76

​

77 for ( int i = 0; i < size; i++ )

​

78 ptr[ i ] = init.ptr[ i ]; // copy init into object

​

79 }

​

80

​

81 // Destructor for class Array

​

82 Array::~Array()

​

83 {

​

84 delete [] ptr; // reclaim space for array

​

85 --arrayCount; // one fewer object

​

86 }

​

87

​

88 // Get the size of the array

​

89 int Array::getSize() const { return size; }

​

90

​

91 // Overloaded assignment operator

​

92 // const return avoids: ( a1 = a2 ) = a3

​

93 const Array &Array::operator=( const Array &right )

​

94 {

​

95 if ( &right != this ) { // check for self-assignment

​

96

​

97 // for arrays of different sizes, deallocate original

​

98 // left side array, then allocate new left side array.

​

99 if ( size != right.size ) {

​

100 delete [] ptr; // reclaim space

​

19 of 47

101 size = right.size; // resize this object

​

102 ptr = new int[ size ]; // create space for array copy

​

103 assert( ptr != 0 ); // terminate if not allocated

​

104 }

​

105

​

106 for ( int i = 0; i < size; i++ )

​

107 ptr[ i ] = right.ptr[ i ]; // copy array into object

​

108 }

​

109

​

110 return *this; // enables x = y = z;

​

111 }

​

112

​

113 // Determine if two arrays are equal and

​

114 // return true, otherwise return false.

​

115 bool Array::operator==( const Array &right ) const

​

116 {

​

117 if ( size != right.size )

​

118 return false; // arrays of different sizes

​

119

​

120 for ( int i = 0; i < size; i++ )

​

121 if ( ptr[ i ] != right.ptr[ i ] )

​

122 return false; // arrays are not equal

​

123

​

124 return true; // arrays are equal

​

125 }

​

126

​

127 // Overloaded subscript operator for non-const Arrays

​

128 // reference return creates an lvalue

​

129 int &Array::operator[]( int subscript )

​

130 {

​

131 // check for subscript out of range error

​

132 assert( 0 <= subscript && subscript < size );

​

20 of 47

133

​

134 return ptr[ subscript ]; // reference return

​

135 }

​

136

​

137 // Overloaded subscript operator for const Arrays

​

138 // const reference return creates an rvalue

​

139 const int &Array::operator[]( int subscript ) const

​

140 {

​

141 // check for subscript out of range error

​

142 assert( 0 <= subscript && subscript < size );

​

143

​

144 return ptr[ subscript ]; // const reference return

​

145 }

​

146

​

147 // Return the number of Array objects instantiated

​

148 // static functions cannot be const

​

149 int Array::getArrayCount() { return arrayCount; }

​

150

​

151 // Overloaded input operator for class Array;

​

152 // inputs values for entire array.

​

153 istream &operator>>( istream &input, Array &a )

​

154 {

​

155 for ( int i = 0; i < a.size; i++ )

​

156 input >> a.ptr[ i ];

​

157

​

158 return input; // enables cin >> x >> y;

​

159 }

​

160

​

161 // Overloaded output operator for class Array

​

162 ostream &operator<<( ostream &output, const Array &a )

​

163 {

​

21 of 47

164 int i;

​

165

​

166 for ( i = 0; i < a.size; i++ ) {

​

167 output << setw( 12 ) << a.ptr[ i ];

​

168

​

169 if ( ( i + 1 ) % 4 == 0 ) // 4 numbers per row of output

​

170 output << endl;

​

171 }

​

172

​

173 if ( i % 4 != 0 )

​

174 output << endl;

​

175

​

176 return output; // enables cout << x << y;

​

177 }

​

178 // Fig. 8.4: fig08_04.cpp

​

179 // Driver for simple class Array

​

180 #include <iostream>

​

181

​

182 using std::cout;

​

183 using std::cin;

​

184 using std::endl;

​

185

​

186 #include "array1.h"

​

187

​

188 int main()

​

189 {

​

190 // no objects yet

​

191 cout << "# of arrays instantiated = "

​

192 << Array::getArrayCount() << '\n';

​

193

​

# of arrays instantiated = 0

22 of 47

194 // create two arrays and print Array count

​

195 Array integers1( 7 ), integers2;

​

196 cout << "# of arrays instantiated = "

​

197 << Array::getArrayCount() << "\n\n";

​

198

​

199 // print integers1 size and contents

​

200 cout << "Size of array integers1 is "

​

201 << integers1.getSize()

​

202 << "\nArray after initialization:\n"

​

203 << integers1 << '\n';

​

204

​

205 // print integers2 size and contents

​

206 cout << "Size of array integers2 is "

​

207 << integers2.getSize()

​

208 << "\nArray after initialization:\n"

​

209 << integers2 << '\n';

​

210

​

211 // input and print integers1 and integers2

​

212 cout << "Input 17 integers:\n";

​

213 cin >> integers1 >> integers2;

​

214 cout << "After input, the arrays contain:\n"

​

215 << "integers1:\n" << integers1

​

216 << "integers2:\n" << integers2 << '\n';

​

217

​

218 // use overloaded inequality (!=) operator

​

219 cout << "Evaluating: integers1 != integers2\n";

​

220 if ( integers1 != integers2 )

​

221 cout << "They are not equal\n";

​

222

​

223 // create array integers3 using integers1 as an

​

224 // initializer; print size and contents

​

225 Array integers3( integers1 );

​

226

​

23 of 47

227 cout << "\nSize of array integers3 is "

​

228 << integers3.getSize()

​

229 << "\nArray after initialization:\n"

​

230 << integers3 << '\n';

​

231

​

232 // use overloaded assignment (=) operator

​

233 cout << "Assigning integers2 to integers1:\n";

​

234 integers1 = integers2;

​

235 cout << "integers1:\n" << integers1

​

236 << "integers2:\n" << integers2 << '\n';

​

237

​

238 // use overloaded equality (==) operator

​

239 cout << "Evaluating: integers1 == integers2\n";

​

240 if ( integers1 == integers2 )

​

241 cout << "They are equal\n\n";

​

242

​

243 // use overloaded subscript operator to create rvalue

​

244 cout << "integers1[5] is " << integers1[ 5 ] << '\n';

​

245

​

246 // use overloaded subscript operator to create lvalue

​

247 cout << "Assigning 1000 to integers1[5]\n";

​

248 integers1[ 5 ] = 1000;

​

249 cout << "integers1:\n" << integers1 << '\n';

​

250

​

251 // attempt to use out of range subscript

​

252 cout << "Attempt to assign 1000 to integers1[15]" << endl;

​

253 integers1[ 15 ] = 1000; // ERROR: out of range

​

254

​

255 return 0;

​

256 }

​

24 of 47

# of arrays instantiated = 0

# of arrays instantiated = 2

Size of array integers1 is 7

Array after initialization:

0 0 0 0

0 0 0

Size of array integers2 is 10

Array after initialization:

0 0 0 0

0 0 0 0

0 0

Input 17 integers:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

After input, the arrays contain:

integers1:

1 2 3 4

5 6 7

integers2:

8 9 10 11

12 13 14 15

16 17

Evaluating: integers1 != integers2

They are not equal

Size of array integers3 is 7

Array after initialization:

1 2 3 4

5 6 7

​

25 of 47

Evaluating: integers1 == integers2

They are equal

integers1[5] is 13

Assigning 1000 to integers1[5]

integers1:

8 9 10 11

12 1000 14 15

16 17

Attempt to assign 1000 to integers1[15]

Assertion failed: 0 <= subscript && subscript < size, file Array1.cpp, line 95 abnormal program termination

Assigning integers2 to integers1:

integers1:

8 9 10 11

12 13 14 15

16 17

integers2:

8 9 10 11

12 13 14 15

16 17

26 of 47

Converting between Types

  • Cast operator
    • Forces conversions among built-in types
    • Specifies conversions between user defined and built-in types
    • Conversion operator must be a non-static member function
    • Cannot be a friend function
    • Do not specify return type
      • Return type is the type to which the object is being converted
    • For user-defined class A

A::operator char *() const;

      • Declares an overloaded cast operator function for creating a char * out of an A object

27 of 47

Converting between Types

A::operator int() const;

      • Declares an overloaded cast operator function for converting an object of A into an integer

A::operator otherClass() const;

      • Declares an overloaded cast operator function for converting an object of A into an object of otherClass
  • Compiler and casting
    • Casting can prevent the need for overloading
    • If an object s of user-defined class String appears in a program where an ordinary char * is expected, such as

cout << s;

The compiler calls the overloaded cast operator function operator char * to convert the object into a char * and uses the resulting char * in the expression

28 of 47

Case Study: A String Class

  • Build a class to handle strings
    • Class string in standard library
  • Conversion constructor
    • Single-argument constructors that turn objects of other types into class objects

29 of 47

1 // Fig. 8.5: string1.h

​

2 // Definition of a String class

​

3 #ifndef STRING1_H

​

4 #define STRING1_H

​

5

​

6 #include <iostream>

​

7

​

8 using std::ostream;

​

9 using std::istream;

​

10

​

11 class String {

​

12 friend ostream &operator<<( ostream &, const String & );

​

13 friend istream &operator>>( istream &, String & );

​

14

​

15 public:

​

16 String( const char * = "" ); // conversion/default ctor

​

17 String( const String & ); // copy constructor

​

18 ~String(); // destructor

​

19 const String &operator=( const String & ); // assignment

​

20 const String &operator+=( const String & ); // concatenation

​

21 bool operator!() const; // is String empty?

​

22 bool operator==( const String & ) const; // test s1 == s2

​

23 bool operator<( const String & ) const; // test s1 < s2

​

24

​

25 // test s1 != s2

​

26 bool operator!=( const String & right ) const

​

27 { return !( *this == right ); }

​

28

​

29 // test s1 > s2

​

30 bool operator>( const String &right ) const

​

31 { return right < *this; }

​

32

​

33 // test s1 <= s2

​

30 of 47

34 bool operator<=( const String &right ) const

​

35 { return !( right < *this ); }

​

36

​

37 // test s1 >= s2

​

38 bool operator>=( const String &right ) const

​

39 { return !( *this < right ); }

​

40

​

41 char &operator[]( int ); // subscript operator

​

42 const char &operator[]( int ) const; // subscript operator

​

43 String operator()( int, int ); // return a substring

​

44 int getLength() const; // return string length

​

45

​

46 private:

​

47 int length; // string length

​

48 char *sPtr; // pointer to start of string

​

49

​

50 void setString( const char * ); // utility function

​

51 };

​

52

​

53 #endif

​

54 // Fig. 8.5: string1.cpp

​

55 // Member function definitions for class String

​

56 #include <iostream>

​

57

​

58 using std::cout;

​

59 using std::endl;

​

60

​

61 #include <iomanip>

​

62

​

63 using std::setw;

​

64

​

31 of 47

65 #include <cstring>

​

66 #include <cassert>

​

67 #include "string1.h"

​

68

​

69 // Conversion constructor: Convert char * to String

​

70 String::String( const char *s ) : length( strlen( s ) )

​

71 {

​

72 cout << "Conversion constructor: " << s << '\n';

​

73 setString( s ); // call utility function

​

74 }

​

75

​

76 // Copy constructor

​

77 String::String( const String &copy ) : length( copy.length )

​

78 {

​

79 cout << "Copy constructor: " << copy.sPtr << '\n';

​

80 setString( copy.sPtr ); // call utility function

​

81 }

​

82

​

83 // Destructor

​

84 String::~String()

​

85 {

​

86 cout << "Destructor: " << sPtr << '\n';

​

87 delete [] sPtr; // reclaim string

​

88 }

​

89

​

90 // Overloaded = operator; avoids self assignment

​

91 const String &String::operator=( const String &right )

​

92 {

​

93 cout << "operator= called\n";

​

94

​

95 if ( &right != this ) { // avoid self assignment

​

Conversion constructor: char * to String.

Constructors and destructors will print when called.

32 of 47

96 delete [] sPtr; // prevents memory leak

​

97 length = right.length; // new String length

​

98 setString( right.sPtr ); // call utility function

​

99 }

​

100 else

​

101 cout << "Attempted assignment of a String to itself\n";

​

102

​

103 return *this; // enables cascaded assignments

​

104 }

​

105

​

106 // Concatenate right operand to this object and

​

107 // store in this object.

​

108 const String &String::operator+=( const String &right )

​

109 {

​

110 char *tempPtr = sPtr; // hold to be able to delete

​

111 length += right.length; // new String length

​

112 sPtr = new char[ length + 1 ]; // create space

​

113 assert( sPtr != 0 ); // terminate if memory not allocated

​

114 strcpy( sPtr, tempPtr ); // left part of new String

​

115 strcat( sPtr, right.sPtr ); // right part of new String

​

116 delete [] tempPtr; // reclaim old space

​

117 return *this; // enables cascaded calls

​

118 }

​

119

​

120 // Is this String empty?

​

121 bool String::operator!() const { return length == 0; }

​

122

​

123 // Is this String equal to right String?

​

124 bool String::operator==( const String &right ) const

​

125 { return strcmp( sPtr, right.sPtr ) == 0; }

​

126

​

127 // Is this String less than right String?

​

33 of 47

128 bool String::operator<( const String &right ) const

​

129 { return strcmp( sPtr, right.sPtr ) < 0; }

​

130

​

131 // Return a reference to a character in a String as an lvalue.

​

132 char &String::operator[]( int subscript )

​

133 {

​

134 // First test for subscript out of range

​

135 assert( subscript >= 0 && subscript < length );

​

136

​

137 return sPtr[ subscript ]; // creates lvalue

​

138 }

​

139

​

140 // Return a reference to a character in a String as an rvalue.

​

141 const char &String::operator[]( int subscript ) const

​

142 {

​

143 // First test for subscript out of range

​

144 assert( subscript >= 0 && subscript < length );

​

145

​

146 return sPtr[ subscript ]; // creates rvalue

​

147 }

​

148

​

149 // Return a substring beginning at index and

​

150 // of length subLength

​

151 String String::operator()( int index, int subLength )

​

152 {

​

153 // ensure index is in range and substring length >= 0

​

154 assert( index >= 0 && index < length && subLength >= 0 );

​

155

​

156 // determine length of substring

​

157 int len;

​

158

​

Notice the overloaded function call operator.

34 of 47

159 if ( ( subLength == 0 ) || ( index + subLength > length ) )

​

160 len = length - index;

​

161 else

​

162 len = subLength;

​

163

​

164 // allocate temporary array for substring and

​

165 // terminating null character

​

166 char *tempPtr = new char[ len + 1 ];

​

167 assert( tempPtr != 0 ); // ensure space allocated

​

168

​

169 // copy substring into char array and terminate string

​

170 strncpy( tempPtr, &sPtr[ index ], len );

​

171 tempPtr[ len ] = '\0';

​

172

​

173 // Create temporary String object containing the substring

​

174 String tempString( tempPtr );

​

175 delete [] tempPtr; // delete the temporary array

​

176

​

177 return tempString; // return copy of the temporary String

​

178 }

​

179

​

180 // Return string length

​

181 int String::getLength() const { return length; }

​

182

​

183 // Utility function to be called by constructors and

​

184 // assignment operator.

​

185 void String::setString( const char *string2 )

​

186 {

​

187 sPtr = new char[ length + 1 ]; // allocate storage

​

188 assert( sPtr != 0 ); // terminate if memory not allocated

​

189 strcpy( sPtr, string2 ); // copy literal to object

​

190 }

​

35 of 47

191

​

192 // Overloaded output operator

​

193 ostream &operator<<( ostream &output, const String &s )

​

194 {

​

195 output << s.sPtr;

​

196 return output; // enables cascading

​

197 }

​

198

​

199 // Overloaded input operator

​

200 istream &operator>>( istream &input, String &s )

​

201 {

​

202 char temp[ 100 ]; // buffer to store input

​

203

​

204 input >> setw( 100 ) >> temp;

​

205 s = temp; // use String class assignment operator

​

206 return input; // enables cascading

​

207 }

​

208 // Fig. 8.5: fig08_05.cpp

​

209 // Driver for class String

​

210 #include <iostream>

​

211

​

212 using std::cout;

​

213 using std::endl;

​

214

​

215 #include "string1.h"

​

216

​

217 int main()

​

218 {

​

219 String s1( "happy" ), s2( " birthday" ), s3;

​

220

​

Conversion constructor: happy

Conversion constructor: birthday

Conversion constructor:

36 of 47

221 // test overloaded equality and relational operators

​

222 cout << "s1 is \"" << s1 << "\"; s2 is \"" << s2

​

223 << "\"; s3 is \"" << s3 << '\"'

​

224 << "\nThe results of comparing s2 and s1:"

​

225 << "\ns2 == s1 yields "

​

226 << ( s2 == s1 ? "true" : "false" )

​

227 << "\ns2 != s1 yields "

​

228 << ( s2 != s1 ? "true" : "false" )

​

229 << "\ns2 > s1 yields "

​

230 << ( s2 > s1 ? "true" : "false" )

​

231 << "\ns2 < s1 yields "

​

232 << ( s2 < s1 ? "true" : "false" )

​

233 << "\ns2 >= s1 yields "

​

234 << ( s2 >= s1 ? "true" : "false" )

​

235 << "\ns2 <= s1 yields "

​

236 << ( s2 <= s1 ? "true" : "false" );

​

237

​

238 // test overloaded String empty (!) operator

​

239 cout << "\n\nTesting !s3:\n";

​

240 if ( !s3 ) {

​

241 cout << "s3 is empty; assigning s1 to s3;\n";

​

242 s3 = s1; // test overloaded assignment

​

243 cout << "s3 is \"" << s3 << "\"";

​

244 }

​

245

​

246 // test overloaded String concatenation operator

​

247 cout << "\n\ns1 += s2 yields s1 = ";

​

248 s1 += s2; // test overloaded concatenation

​

249 cout << s1;

​

250

​

251 // test conversion constructor

​

252 cout << "\n\ns1 += \" to you\" yields\n";

​

253 s1 += " to you"; // test conversion constructor

​

37 of 47

254 cout << "s1 = " << s1 << "\n\n";

​

255

​

256 // test overloaded function call operator () for substring

​

257 cout << "The substring of s1 starting at\n"

​

258 << "location 0 for 14 characters, s1(0, 14), is:\n"

​

259 << s1( 0, 14 ) << "\n\n";

​

260

​

261 // test substring "to-end-of-String" option

​

262 cout << "The substring of s1 starting at\n"

​

263 << "location 15, s1(15, 0), is: "

​

264 << s1( 15, 0 ) << "\n\n"; // 0 is "to end of string"

​

265

​

266 // test copy constructor

​

267 String *s4Ptr = new String( s1 );

​

268 cout << "*s4Ptr = " << *s4Ptr << "\n\n";

​

269

​

270 // test assignment (=) operator with self-assignment

​

271 cout << "assigning *s4Ptr to *s4Ptr\n";

​

272 *s4Ptr = *s4Ptr; // test overloaded assignment

​

273 cout << "*s4Ptr = " << *s4Ptr << '\n';

​

274

​

275 // test destructor

​

276 delete s4Ptr;

​

277

​

278 // test using subscript operator to create lvalue

​

279 s1[ 0 ] = 'H';

​

280 s1[ 6 ] = 'B';

​

281 cout << "\ns1 after s1[0] = 'H' and s1[6] = 'B' is: "

​

282 << s1 << "\n\n";

​

283

​

s1 = happy birthday to you

38 of 47

284 // test subscript out of range

​

285 cout << "Attempt to assign 'd' to s1[30] yields:" << endl;

​

286 s1[ 30 ] = 'd'; // ERROR: subscript out of range

​

287

​

288 return 0;

​

289 }

​

Conversion constructor: happy

Conversion constructor: birthday

Conversion constructor:

s1 is "happy"; s2 is " birthday"; s3 is ""

The results of comparing s2 and s1:

s2 == s1 yields false

s2 != s1 yields true

s2 > s1 yields false

s2 < s1 yields true

s2 >= s1 yields false

s2 <= s1 yields true

 

Testing !s3:

s3 is empty; assigning s1 to s3;

operator= called

s3 is "happy"

s1 += s2 yields s1 = happy birthday

 

s1 += " to you" yields

Conversion constructor: to you

Destructor: to you

s1 = happy birthday to you

Attempt to assign 'd' to s1[30] yields:

 

Assertion failed: subscript >= 0 && subscript < length, file string1.cpp, line 82

 

Abnormal program termination

39 of 47

​

Program Output

Conversion constructor: happy birthday

Copy constructor: happy birthday

Destructor: happy birthday

The substring of s1 starting at

location 0 for 14 characters, s1(0, 14), is:

happy birthday

 

Destructor: happy birthday

Conversion constructor: to you

Copy constructor: to you

Destructor: to you

The substring of s1 starting at

location 15, s1(15, 0), is: to you

 

Destructor: to you

Copy constructor: happy birthday to you

*s4Ptr = happy birthday to you

 

assigning *s4Ptr to *s4Ptr

operator= called

Attempted assignment of a String to itself

*s4Ptr = happy birthday to you

Destructor: happy birthday to you

 

s1 after s1[0] = 'H' and s1[6] = 'B' is: Happy Birthday to you

 

Attempt to assign 'd' to s1[30] yields:

 

Assertion failed: subscript >= 0 && subscript < length, file string1.cpp, line 82

 

Abnormal program termination

​

40 of 47

Overloading ++ and --

  • Pre/post incrementing/decrementing operators
    • Allowed to be overloaded
    • Distinguishing between pre and post operators
      • prefix versions are overloaded the same as other prefix unary operators

d1.operator++(); // for ++d1

      • convention adopted that when compiler sees postincrementing expression, it will generate the member-function call

d1.operator++( 0 ); // for d1++

      • 0 is a dummy value to make the argument list of operator++ distinguishable from the argument list for ++operator

41 of 47

Case Study: A Date Class

  • The following example creates a Date class with
    • An overloaded increment operator to change the day, month and year
    • An overloaded += operator
    • A function to test for leap years
    • A function to determine if a day is last day of a month

42 of 47

1 // Fig. 8.6: date1.h

​

2 // Definition of class Date

​

3 #ifndef DATE1_H

​

4 #define DATE1_H

​

5 #include <iostream>

​

6

​

7 using std::ostream;

​

8

​

9 class Date {

​

10 friend ostream &operator<<( ostream &, const Date & );

​

11

​

12 public:

​

13 Date( int m = 1, int d = 1, int y = 1900 ); // constructor

​

14 void setDate( int, int, int ); // set the date

​

15 Date &operator++(); // preincrement operator

​

16 Date operator++( int ); // postincrement operator

​

17 const Date &operator+=( int ); // add days, modify object

​

18 bool leapYear( int ) const; // is this a leap year?

​

19 bool endOfMonth( int ) const; // is this end of month?

​

20

​

21 private:

​

22 int month;

​

23 int day;

​

24 int year;

​

25

​

26 static const int days[]; // array of days per month

​

27 void helpIncrement(); // utility function

​

28 };

​

29

​

30 #endif

​

43 of 47

31 // Fig. 8.6: date1.cpp

​

32 // Member function definitions for Date class

​

33 #include <iostream>

​

34 #include "date1.h"

​

35

​

36 // Initialize static member at file scope;

​

37 // one class-wide copy.

​

38 const int Date::days[] = { 0, 31, 28, 31, 30, 31, 30,

​

39 31, 31, 30, 31, 30, 31 };

​

40

​

41 // Date constructor

​

42 Date::Date( int m, int d, int y ) { setDate( m, d, y ); }

​

43

​

44 // Set the date

​

45 void Date::setDate( int mm, int dd, int yy )

​

46 {

​

47 month = ( mm >= 1 && mm <= 12 ) ? mm : 1;

​

48 year = ( yy >= 1900 && yy <= 2100 ) ? yy : 1900;

​

49

​

50 // test for a leap year

​

51 if ( month == 2 && leapYear( year ) )

​

52 day = ( dd >= 1 && dd <= 29 ) ? dd : 1;

​

53 else

​

54 day = ( dd >= 1 && dd <= days[ month ] ) ? dd : 1;

​

55 }

​

56

​

57 // Preincrement operator overloaded as a member function.

​

58 Date &Date::operator++()

​

59 {

​

60 helpIncrement();

​

61 return *this; // reference return to create an lvalue

​

62 }

​

63

​

44 of 47

64 // Postincrement operator overloaded as a member function.

​

65 // Note that the dummy integer parameter does not have a

​

66 // parameter name.

​

67 Date Date::operator++( int )

​

68 {

​

69 Date temp = *this;

​

70 helpIncrement();

​

71

​

72 // return non-incremented, saved, temporary object

​

73 return temp; // value return; not a reference return

​

74 }

​

75

​

76 // Add a specific number of days to a date

​

77 const Date &Date::operator+=( int additionalDays )

​

78 {

​

79 for ( int i = 0; i < additionalDays; i++ )

​

80 helpIncrement();

​

81

​

82 return *this; // enables cascading

​

83 }

​

84

​

85 // If the year is a leap year, return true;

​

86 // otherwise, return false

​

87 bool Date::leapYear( int y ) const

​

88 {

​

89 if ( y % 400 == 0 || ( y % 100 != 0 && y % 4 == 0 ) )

​

90 return true; // a leap year

​

91 else

​

92 return false; // not a leap year

​

93 }

​

94

​

95 // Determine if the day is the end of the month

​

96 bool Date::endOfMonth( int d ) const

​

97 {

​

postincrement operator has a dummy int value.

45 of 47

98 if ( month == 2 && leapYear( year ) )

​

99 return d == 29; // last day of Feb. in leap year

​

100 else

​

101 return d == days[ month ];

​

102 }

​

103

​

104 // Function to help increment the date

​

105 void Date::helpIncrement()

​

106 {

​

107 if ( endOfMonth( day ) && month == 12 ) { // end year

​

108 day = 1;

​

109 month = 1;

​

110 ++year;

​

111 }

​

112 else if ( endOfMonth( day ) ) { // end month

​

113 day = 1;

​

114 ++month;

​

115 }

​

116 else // not end of month or year; increment day

​

117 ++day;

​

118 }

​

119

​

120 // Overloaded output operator

​

121 ostream &operator<<( ostream &output, const Date &d )

​

122 {

​

123 static char *monthName[ 13 ] = { "", "January",

​

124 "February", "March", "April", "May", "June",

​

125 "July", "August", "September", "October",

​

126 "November", "December" };

​

127

​

128 output << monthName[ d.month ] << ' '

​

129 << d.day << ", " << d.year;

​

130

​

131 return output; // enables cascading

​

132 }

​

46 of 47

133 // Fig. 8.6: fig08_06.cpp

​

134 // Driver for class Date

​

135 #include <iostream>

​

136

​

137 using std::cout;

​

138 using std::endl;

​

139

​

140 #include "date1.h"

​

141

​

142 int main()

​

143 {

​

144 Date d1, d2( 12, 27, 1992 ), d3( 0, 99, 8045 );

​

145 cout << "d1 is " << d1

​

146 << "\nd2 is " << d2

​

147 << "\nd3 is " << d3 << "\n\n";

​

148

​

149 cout << "d2 += 7 is " << ( d2 += 7 ) << "\n\n";

​

150

​

151 d3.setDate( 2, 28, 1992 );

​

152 cout << " d3 is " << d3;

​

153 cout << "\n++d3 is " << ++d3 << "\n\n";

​

154

​

155 Date d4( 3, 18, 1969 );

​

156

​

157 cout << "Testing the preincrement operator:\n"

​

158 << " d4 is " << d4 << '\n';

​

159 cout << "++d4 is " << ++d4 << '\n';

​

160 cout << " d4 is " << d4 << "\n\n";

​

161

​

162 cout << "Testing the postincrement operator:\n"

​

163 << " d4 is " << d4 << '\n';

​

164 cout << "d4++ is " << d4++ << '\n';

​

165 cout << " d4 is " << d4 << endl;

​

166

​

167 return 0;

​

168 }

​

47 of 47

d1 is January 1, 1900

d2 is December 27, 1992

d3 is January 1, 1900

d2 += 7 is January 3, 1993

d3 is February 28, 1992

++d3 is February 29, 1992

Testing the preincrement operator:

d4 is March 18, 1969

++d4 is March 19, 1969

d4 is March 19, 1969

Testing the postincrement operator:

d4 is March 19, 1969

d4++ is March 19, 1969

d4 is March 20, 1969

​