47
Operator Overloading

Operator Overloading. Introduction Operator overloading –Enabling C++’s operators to work with class objects –Using traditional operators with user-defined

Embed Size (px)

Citation preview

Operator Overloading

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

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

Restrictions on Operator Overloading

• C++ operators that can be overloaded

• C++ Operators that cannot be overloaded

Operators that cannot be overloaded

. .* :: ?: sizeof

Operators that can be overloaded

+ - * / % ^ & |

~ ! = < > += -= *=

/= %= ^= &= |= << >> >>=

<<= == != <= >= && || ++

-- ->* , -> [] () new delete

new[] delete[]

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

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

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

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 {

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 }

Program Output

Enter phone number in the form (123) 456-7890:(800) 555-1212The phone number entered was: (800) 555-1212

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; ...};

Overloading Unary Operators

– Example declaration as a non-member function class String { friend bool operator!( const String & ) ...}

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 )

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 )

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 !=

1 // Fig. 8.4: array1.h2 // Simple class Array (for integers)3 #ifndef ARRAY1_H4 #define ARRAY1_H56 #include <iostream>78 using std::ostream;9 using std::istream;1011 class Array {12 friend ostream &operator<<( ostream &, const Array & );13 friend istream &operator>>( istream &, Array & );14 public:15 Array( int = 10 ); // default constructor16 Array( const Array & ); // copy constructor17 ~Array(); // destructor18 int getSize() const; // return size19 const Array &operator=( const Array & ); // assign arrays20 bool operator==( const Array & ) const; // compare equal2122 // Determine if two arrays are not equal and23 // return true, otherwise return false (uses operator==).24 bool operator!=( const Array &right ) const 25 { return ! ( *this == right ); }26 27 int &operator[]( int ); // subscript operator28 const int &operator[]( int ) const; // subscript operator29 static int getArrayCount(); // Return count of 30 // arrays instantiated.31 private:32 int size; // size of the array33 int *ptr; // pointer to first element of array34 static int arrayCount; // # of Arrays instantiated

35 };3637 #endif38 // Fig 8.4: array1.cpp39 // Member function definitions for class Array40 #include <iostream>

41

42 using std::cout;43 using std::cin;

44 using std::endl;45

46 #include <iomanip>47

48 using std::setw;

4950 #include <cstdlib>51 #include <cassert>

52 #include "array1.h"

5354 // Initialize static data member at file scope

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

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 allocated63 ++arrayCount; // count one more object64

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

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

67 }6869 // Copy constructor for class Array70 // must receive a reference to prevent infinite recursion71 Array::Array( const Array &init ) : size( init.size )72 {73 ptr = new int[ size ]; // create space for array74 assert( ptr != 0 ); // terminate if memory not allocated75 ++arrayCount; // count one more object7677 for ( int i = 0; i < size; i++ )78 ptr[ i ] = init.ptr[ i ]; // copy init into object79 }8081 // Destructor for class Array82 Array::~Array()83 {84 delete [] ptr; // reclaim space for array85 --arrayCount; // one fewer object86 }8788 // Get the size of the array89 int Array::getSize() const { return size; }9091 // Overloaded assignment operator92 // const return avoids: ( a1 = a2 ) = a393 const Array &Array::operator=( const Array &right )94 {95 if ( &right != this ) { // check for self-assignment96 97 // for arrays of different sizes, deallocate original98 // left side array, then allocate new left side array.99 if ( size != right.size ) {100 delete [] ptr; // reclaim space

101 size = right.size; // resize this object102 ptr = new int[ size ]; // create space for array copy103 assert( ptr != 0 ); // terminate if not allocated104 }105106 for ( int i = 0; i < size; i++ )107 ptr[ i ] = right.ptr[ i ]; // copy array into object108 }109110 return *this; // enables x = y = z;111}112113// Determine if two arrays are equal and114// return true, otherwise return false.115bool Array::operator==( const Array &right ) const116{117 if ( size != right.size )118 return false; // arrays of different sizes119120 for ( int i = 0; i < size; i++ )121 if ( ptr[ i ] != right.ptr[ i ] )122 return false; // arrays are not equal123124 return true; // arrays are equal125}126127// Overloaded subscript operator for non-const Arrays128// reference return creates an lvalue129int &Array::operator[]( int subscript )130{131 // check for subscript out of range error132 assert( 0 <= subscript && subscript < size );

133

134 return ptr[ subscript ]; // reference return

135}

136

137// Overloaded subscript operator for const Arrays

138// const reference return creates an rvalue

139const 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

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

150

151// Overloaded input operator for class Array;

152// inputs values for entire array.

153istream &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

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

163{

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

182using std::cout;

183using std::cin;

184using std::endl;

185

186#include "array1.h"

187

188int main()

189{

190 // no objects yet

191 cout << "# of arrays instantiated = "

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

193

# of arrays instantiated = 0

194 // create two arrays and print Array count195 Array integers1( 7 ), integers2;196 cout << "# of arrays instantiated = "197 << Array::getArrayCount() << "\n\n";198199 // print integers1 size and contents200 cout << "Size of array integers1 is "201 << integers1.getSize()202 << "\nArray after initialization:\n"203 << integers1 << '\n';204205 // print integers2 size and contents206 cout << "Size of array integers2 is "207 << integers2.getSize()208 << "\nArray after initialization:\n"209 << integers2 << '\n';210211 // input and print integers1 and integers2212 cout << "Input 17 integers:\n";213 cin >> integers1 >> integers2;214 cout << "After input, the arrays contain:\n"215 << "integers1:\n" << integers1216 << "integers2:\n" << integers2 << '\n';217218 // use overloaded inequality (!=) operator219 cout << "Evaluating: integers1 != integers2\n";220 if ( integers1 != integers2 )221 cout << "They are not equal\n";222223 // create array integers3 using integers1 as an224 // initializer; print size and contents225 Array integers3( integers1 );226

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}

# of arrays instantiated = 0# of arrays instantiated = 2 Size of array integers1 is 7Array after initialization: 0 0 0 0 0 0 0 Size of array integers2 is 10Array 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 17After input, the arrays contain:integers1: 1 2 3 4 5 6 7integers2: 8 9 10 11 12 13 14 15 16 17 Evaluating: integers1 != integers2They are not equal Size of array integers3 is 7Array after initialization: 1 2 3 4 5 6 7

Evaluating: integers1 == integers2They are equal integers1[5] is 13Assigning 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 17integers2: 8 9 10 11 12 13 14 15 16 17

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 AA::operator char *() const;

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

Converting between TypesA::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

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

1 // Fig. 8.5: string1.h2 // Definition of a String class3 #ifndef STRING1_H4 #define STRING1_H56 #include <iostream>78 using std::ostream;9 using std::istream;1011 class String {12 friend ostream &operator<<( ostream &, const String & );13 friend istream &operator>>( istream &, String & );1415 public:16 String( const char * = "" ); // conversion/default ctor17 String( const String & ); // copy constructor18 ~String(); // destructor19 const String &operator=( const String & ); // assignment20 const String &operator+=( const String & ); // concatenation21 bool operator!() const; // is String empty?22 bool operator==( const String & ) const; // test s1 == s223 bool operator<( const String & ) const; // test s1 < s22425 // test s1 != s226 bool operator!=( const String & right ) const27 { return !( *this == right ); }2829 // test s1 > s230 bool operator>( const String &right ) const31 { return right < *this; }32 33 // test s1 <= s2

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.cpp55 // Member function definitions for class String56 #include <iostream>5758 using std::cout;59 using std::endl;6061 #include <iomanip>6263 using std::setw;64

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.

96 delete [] sPtr; // prevents memory leak97 length = right.length; // new String length98 setString( right.sPtr ); // call utility function99 }100 else101 cout << "Attempted assignment of a String to itself\n";102103 return *this; // enables cascaded assignments104}105106// Concatenate right operand to this object and107// store in this object.108const String &String::operator+=( const String &right )109{110 char *tempPtr = sPtr; // hold to be able to delete111 length += right.length; // new String length112 sPtr = new char[ length + 1 ]; // create space113 assert( sPtr != 0 ); // terminate if memory not allocated114 strcpy( sPtr, tempPtr ); // left part of new String115 strcat( sPtr, right.sPtr ); // right part of new String116 delete [] tempPtr; // reclaim old space117 return *this; // enables cascaded calls118}119120// Is this String empty?121bool String::operator!() const { return length == 0; }122123// Is this String equal to right String?124bool String::operator==( const String &right ) const125 { return strcmp( sPtr, right.sPtr ) == 0; }126127// Is this String less than right String?

128bool 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.

132char &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.

141const 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

151String 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.

159 if ( ( subLength == 0 ) || ( index + subLength > length ) )160 len = length - index;161 else162 len = subLength;163164 // allocate temporary array for substring and 165 // terminating null character166 char *tempPtr = new char[ len + 1 ];167 assert( tempPtr != 0 ); // ensure space allocated168169 // copy substring into char array and terminate string170 strncpy( tempPtr, &sPtr[ index ], len );171 tempPtr[ len ] = '\0';172173 // Create temporary String object containing the substring174 String tempString( tempPtr );175 delete [] tempPtr; // delete the temporary array176177 return tempString; // return copy of the temporary String178}179180// Return string length181int String::getLength() const { return length; }182183// Utility function to be called by constructors and 184// assignment operator.185void String::setString( const char *string2 )186{187 sPtr = new char[ length + 1 ]; // allocate storage188 assert( sPtr != 0 ); // terminate if memory not allocated189 strcpy( sPtr, string2 ); // copy literal to object190}

191

192// Overloaded output operator

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

194{

195 output << s.sPtr;

196 return output; // enables cascading

197}

198

199// Overloaded input operator

200istream &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

212using std::cout;

213using std::endl;

214

215#include "string1.h"

216

217int main()

218{

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

220

Conversion constructor: happy

Conversion constructor: birthday

Conversion constructor:

221 // test overloaded equality and relational operators222 cout << "s1 is \"" << s1 << "\"; s2 is \"" << s2223 << "\"; 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" );237238 // test overloaded String empty (!) operator239 cout << "\n\nTesting !s3:\n";240 if ( !s3 ) {241 cout << "s3 is empty; assigning s1 to s3;\n";242 s3 = s1; // test overloaded assignment243 cout << "s3 is \"" << s3 << "\"";244 }245246 // test overloaded String concatenation operator247 cout << "\n\ns1 += s2 yields s1 = ";248 s1 += s2; // test overloaded concatenation249 cout << s1;250251 // test conversion constructor252 cout << "\n\ns1 += \" to you\" yields\n";253 s1 += " to you"; // test conversion constructor

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

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: happyConversion constructor: birthdayConversion constructor: s1 is "happy"; s2 is " birthday"; s3 is ""The results of comparing s2 and s1:s2 == s1 yields falses2 != s1 yields trues2 > s1 yields falses2 < s1 yields trues2 >= s1 yields falses2 <= s1 yields true Testing !s3:s3 is empty; assigning s1 to s3;operator= calleds3 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

Program Output

Conversion constructor: happy birthdayCopy constructor: happy birthdayDestructor: happy birthdayThe substring of s1 starting atlocation 0 for 14 characters, s1(0, 14), is:happy birthday Destructor: happy birthdayConversion constructor: to youCopy constructor: to youDestructor: to youThe substring of s1 starting atlocation 15, s1(15, 0), is: to you Destructor: to youCopy constructor: happy birthday to you*s4Ptr = happy birthday to you assigning *s4Ptr to *s4Ptroperator= calledAttempted assignment of a String to itself*s4Ptr = happy birthday to youDestructor: 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

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

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

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

31 // Fig. 8.6: date1.cpp32 // Member function definitions for Date class33 #include <iostream>34 #include "date1.h"3536 // 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 };4041 // Date constructor42 Date::Date( int m, int d, int y ) { setDate( m, d, y ); }4344 // Set the date45 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;4950 // test for a leap year51 if ( month == 2 && leapYear( year ) )52 day = ( dd >= 1 && dd <= 29 ) ? dd : 1;53 else54 day = ( dd >= 1 && dd <= days[ month ] ) ? dd : 1;55 }5657 // Preincrement operator overloaded as a member function.58 Date &Date::operator++()59 {60 helpIncrement();61 return *this; // reference return to create an lvalue62 }63

64 // Postincrement operator overloaded as a member function.65 // Note that the dummy integer parameter does not have a66 // parameter name.67 Date Date::operator++( int )68 {69 Date temp = *this;70 helpIncrement();7172 // return non-incremented, saved, temporary object73 return temp; // value return; not a reference return74 }7576 // Add a specific number of days to a date77 const Date &Date::operator+=( int additionalDays )78 {79 for ( int i = 0; i < additionalDays; i++ )80 helpIncrement();8182 return *this; // enables cascading83 }8485 // If the year is a leap year, return true; 86 // otherwise, return false87 bool Date::leapYear( int y ) const88 {89 if ( y % 400 == 0 || ( y % 100 != 0 && y % 4 == 0 ) )90 return true; // a leap year91 else92 return false; // not a leap year93 }9495 // Determine if the day is the end of the month96 bool Date::endOfMonth( int d ) const97 {

postincrement operator has a dummy int value.

98 if ( month == 2 && leapYear( year ) )99 return d == 29; // last day of Feb. in leap year100 else101 return d == days[ month ];102}103104// Function to help increment the date105void Date::helpIncrement()106{107 if ( endOfMonth( day ) && month == 12 ) { // end year108 day = 1;109 month = 1;110 ++year;111 }112 else if ( endOfMonth( day ) ) { // end month113 day = 1;114 ++month;115 }116 else // not end of month or year; increment day117 ++day;118}119120// Overloaded output operator121ostream &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" };127128 output << monthName[ d.month ] << ' '129 << d.day << ", " << d.year;130131 return output; // enables cascading132}

133// Fig. 8.6: fig08_06.cpp134// Driver for class Date135#include <iostream>136137using std::cout;138using std::endl;139140#include "date1.h"141142int main()143{144 Date d1, d2( 12, 27, 1992 ), d3( 0, 99, 8045 );145 cout << "d1 is " << d1146 << "\nd2 is " << d2147 << "\nd3 is " << d3 << "\n\n";148149 cout << "d2 += 7 is " << ( d2 += 7 ) << "\n\n";150151 d3.setDate( 2, 28, 1992 );152 cout << " d3 is " << d3;153 cout << "\n++d3 is " << ++d3 << "\n\n";154155 Date d4( 3, 18, 1969 );156157 cout << "Testing the preincrement operator:\n"158 << " d4 is " << d4 << '\n';159 cout << "++d4 is " << ++d4 << '\n';160 cout << " d4 is " << d4 << "\n\n";161162 cout << "Testing the postincrement operator:\n"163 << " d4 is " << d4 << '\n';164 cout << "d4++ is " << d4++ << '\n';165 cout << " d4 is " << d4 << endl;166167 return 0;168}

d1 is January 1, 1900d2 is December 27, 1992d3 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, 1969d4++ is March 19, 1969 d4 is March 20, 1969