Operator Overloading
47 Slides489.50 KB
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 Operators that can be overloaded - * / % & ! - * / % & ! && -- - * , - [] () new delete new[] delete[] C Operators that cannot be overloaded Operators that cannot be overloaded . .* :: ?: sizeof
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 nonmember 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 userdefined 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 3 // Overloading the stream-insertion and // stream-extraction operators. 4 5 #include iostream 6 using std::cout; 7 8 using std::cin; 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 18 friend ostream &operator ( ostream&, const PhoneNumber & ); friend istream &operator ( istream&, PhoneNumber & ); 19 20 private: 21 char areaCode[ 4 ]; // 3-digit area code and null 22 23 // 3-digit exchange and null // 4-digit line and null char exchange[ 4 ]; char line[ 5 ]; 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 output "(" num.areaCode ") " num.exchange "-" num.line; return output; // enables cout a b c; } istream &operator ( istream &input, PhoneNumber &num ) { input.ignore(); // skip ( input setw( 4 ) num.areaCode; // input area code input.ignore( 2 ); // skip ) and space input setw( 4 ) num.exchange; // input exchange input.ignore(); // skip dash (-) input setw( 5 ) num.line; // input line return input; // enables cin a b c; } int main() { PhoneNumber phone; // create object phone cout "Enter phone number in the form (123) 456-7890:\n"; // cin phone invokes operator function by // issuing the call operator ( cin, phone ). cin phone; // cout phone invokes operator function by // issuing the call operator ( cout, phone ). cout "The phone number entered was: " phone endl; return 0; }
Enter phone number in the form (123) 456-7890: (800) 555-1212 The phone number entered was: (800) 555-1212 Program Output
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 // Fig. 8.4: array1.h // Simple class Array (for integers) #ifndef ARRAY1 H #define ARRAY1 H #include iostream using std::ostream; using std::istream; class Array { friend ostream &operator ( ostream &, const Array & ); friend istream &operator ( istream &, Array & ); public: Array( int 10 ); // default constructor Array( const Array & ); // copy constructor Array(); // destructor int getSize() const; // return size const Array &operator ( const Array & ); // assign arrays bool operator ( const Array & ) const; // compare equal // Determine if two arrays are not equal and // return true, otherwise return false (uses operator ). bool operator! ( const Array &right ) const { return ! ( *this right ); } int &operator[]( int ); const int &operator[]( int ) const; static int getArrayCount(); // // // // subscript operator subscript operator Return count of arrays instantiated. private: int size; // size of the array int *ptr; // pointer to first element of array static int arrayCount; // # of Arrays instantiated
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 }; #endif // Fig 8.4: array1.cpp // Member function definitions for class Array #include iostream using std::cout; using std::cin; using std::endl; #include iomanip using std::setw; #include cstdlib #include cassert #include "array1.h" // Initialize static data member at file scope int Array::arrayCount 0; // no objects yet // Default constructor for class Array (default size 10) Array::Array( int arraySize ) { size ( arraySize 0 ? arraySize : 10 ); ptr new int[ size ]; // create space for array assert( ptr ! 0 ); // terminate if memory not allocated arrayCount; // count one more object for ( int i 0; i size; i ) ptr[ i ] 0; // initialize array
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
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 );
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 {
164 165 int i; 166 for ( i 0; i a.size; i ) { 167 output setw( 12 ) a.ptr[ i ]; 168 169 170 171 if ( ( i 1 ) % 4 0 ) // 4 numbers per row of output output endl; } 172 173 174 if ( i % 4 ! 0 ) 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 192 cout "# of arrays instantiated " Array::getArrayCount() '\n'; 193 # of arrays instantiated 0
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 // create two arrays and print Array count Array integers1( 7 ), integers2; cout "# of arrays instantiated " Array::getArrayCount() "\n\n"; // print integers1 size and contents cout "Size of array integers1 is " integers1.getSize() "\nArray after initialization:\n" integers1 '\n'; // print integers2 size and contents cout "Size of array integers2 is " integers2.getSize() "\nArray after initialization:\n" integers2 '\n'; // input and print integers1 and integers2 cout "Input 17 integers:\n"; cin integers1 integers2; cout "After input, the arrays contain:\n" "integers1:\n" integers1 "integers2:\n" integers2 '\n'; // use overloaded inequality (! ) operator cout "Evaluating: integers1 ! integers2\n"; if ( integers1 ! integers2 ) cout "They are not equal\n"; // create array integers3 using integers1 as an // initializer; print size and contents Array integers3( integers1 );
227 228 cout "\nSize of array integers3 is " integers3.getSize() 229 "\nArray after initialization:\n" 230 integers3 '\n'; 231 232 233 // use overloaded assignment ( ) operator cout "Assigning integers2 to integers1:\n"; 234 integers1 integers2; 235 cout "integers1:\n" integers1 236 237 "integers2:\n" integers2 '\n'; 238 // use overloaded equality ( ) operator 239 cout "Evaluating: integers1 integers2\n"; 240 241 if ( integers1 integers2 ) 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 250 cout "integers1:\n" integers1 '\n'; 251 // attempt to use out of range subscript 252 cout "Attempt to assign 1000 to integers1[15]" endl; 253 254 integers1[ 15 ] 1000; 255 return 0; 256 } // ERROR: out of range
# of arrays instantiated 0 # of arrays instantiated 2 Size of array integers1 is 7 Array after initialization: 0 0 0 0 Size of array integers2 is 10 Array after initialization: 0 0 0 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 5 6 7 integers2: 8 9 10 12 13 14 16 17 4 11 15 Evaluating: integers1 ! integers2 They are not equal Size of array integers3 is 7 Array after initialization: 1 2 5 6 3 7 4
Assigning integers2 to integers1: integers1: 8 9 12 13 16 17 integers2: 8 9 12 13 16 17 10 14 11 15 10 14 11 15 10 14 11 15 Evaluating: integers1 integers2 They are equal integers1[5] is 13 Assigning 1000 to integers1[5] integers1: 8 9 12 1000 16 17 Attempt to assign 1000 to integers1[15] Assertion failed: 0 subscript && subscript size, file Array1.cpp, line 95 abnormal program termination
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
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
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 // Fig. 8.5: string1.h // Definition of a String class #ifndef STRING1 H #define STRING1 H #include iostream using std::ostream; using std::istream; class String { friend ostream &operator ( ostream &, const String & ); friend istream &operator ( istream &, String & ); public: String( const char * "" ); // conversion/default ctor String( const String & ); // copy constructor String(); // destructor const String &operator ( const String & ); // assignment const String &operator ( const String & ); // concatenation bool operator!() const; // is String empty? bool operator ( const String & ) const; // test s1 s2 bool operator ( const String & ) const; // test s1 s2 // test s1 ! s2 bool operator! ( const String & right ) const { return !( *this right ); } // test s1 s2 bool operator ( const String &right ) const { return right *this; } // test s1 s2
34 35 bool operator ( const String &right ) const { return !( right *this ); } 36 37 // test s1 s2 38 bool operator ( const String &right ) const 39 40 { return !( *this right ); } 41 char &operator[]( int ); // subscript operator 42 const char &operator[]( int ) const; // subscript operator 43 44 String operator()( int, int ); int getLength() const; // return a substring // return string length 45 46 private: 47 48 int length; char *sPtr; // string length // pointer to start of string 49 50 void setString( const char * ); // utility function 51 }; 52 53 #endif 54 55 56 57 58 59 60 61 62 63 64 // Fig. 8.5: string1.cpp // Member function definitions for class String #include iostream using std::cout; using std::endl; #include iomanip using std::setw;
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 #include cstring #include cassert #include "string1.h" // Conversion constructor: Convert char * to String String::String( const char *s ) : length( strlen( s ) ) { cout "Conversion constructor: " s '\n'; setString( s ); // call utility function } Conversion constructor: char * to String. // Copy constructor String::String( const String © ) : length( copy.length ) { cout "Copy constructor: " copy.sPtr '\n'; setString( copy.sPtr ); // call utility function } // Destructor String:: String() { cout "Destructor: " sPtr '\n'; delete [] sPtr; // reclaim string } Constructors and destructors will print when called. // Overloaded operator; avoids self assignment const String &String::operator ( const String &right ) { cout "operator called\n"; if ( &right ! this ) { // avoid self assignment
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?
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 Notice the overloaded 147 } function call operator. 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
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 }
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" Conversion constructor: happy 216 Conversion constructor: 217 int main() Conversion constructor: 218 { 219 220 String s1( "happy" ), s2( " birthday" ), s3; birthday
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 // test cout overloaded equality and relational operators "s1 is \"" s1 "\"; s2 is \"" s2 "\"; s3 is \"" s3 '\"' "\nThe results of comparing s2 and s1:" "\ns2 s1 yields " ( s2 s1 ? "true" : "false" ) "\ns2 ! s1 yields " ( s2 ! s1 ? "true" : "false" ) "\ns2 s1 yields " ( s2 s1 ? "true" : "false" ) "\ns2 s1 yields " ( s2 s1 ? "true" : "false" ) "\ns2 s1 yields " ( s2 s1 ? "true" : "false" ) "\ns2 s1 yields " ( s2 s1 ? "true" : "false" ); // test overloaded String empty (!) operator cout "\n\nTesting !s3:\n"; if ( !s3 ) { cout "s3 is empty; assigning s1 to s3;\n"; s3 s1; // test overloaded assignment cout "s3 is \"" s3 "\""; } // test overloaded String concatenation operator cout "\n\ns1 s2 yields s1 "; s1 s2; // test overloaded concatenation cout s1; // test conversion constructor cout "\n\ns1 \" to you\" yields\n"; s1 " to you"; // test conversion constructor
254 255 cout "s1 " s1 "\n\n"; 256 // test overloaded function call operator () for substring 257 cout "The substring of s1 starting at\n" s1 happy birthday to you 258 "location 0 for 14 characters, s1(0, 14), is:\n" 259 260 s1( 0, 14 ) "\n\n"; 261 // test substring "to-end-of-String" option 262 cout "The substring of s1 starting at\n" 263 264 "location 15, s1(15, 0), is: " s1( 15, 0 ) "\n\n"; // 0 is "to end of string" 265 266 // test copy constructor 267 268 String *s4Ptr new String( s1 ); cout "*s4Ptr " *s4Ptr "\n\n"; 269 270 // test assignment ( ) operator with self-assignment 271 cout "assigning *s4Ptr to *s4Ptr\n"; 272 273 *s4Ptr *s4Ptr; // test overloaded assignment cout "*s4Ptr " *s4Ptr '\n'; 274 275 // test destructor 276 277 delete s4Ptr; 278 // test using subscript operator to create lvalue 279 s1[ 0 ] 'H'; 280 281 s1[ 6 ] 'B'; cout "\ns1 after s1[0] 'H' and s1[6] 'B' is: " 282 283 s1 "\n\n";
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; Attempt to assign 'd' to s1[30] yields: 289 } Conversion constructor: happy Assertion failed: subscript 0 && subscript Conversion constructor: birthday length, file string1.cpp, line 82 Conversion constructor: s1 is "happy"; s2 is " birthday"; s3 is "" The results of comparing s2 and s1: Abnormal program termination 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: Destructor: to you to you s1 happy birthday to you
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 Program Output 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 2 // Fig. 8.6: date1.h // Definition of class Date 3 #ifndef DATE1 H 4 #define DATE1 H 5 #include iostream 6 7 using std::ostream; 8 9 10 11 class Date { friend ostream &operator ( ostream &, const Date & ); 12 public: 13 Date( int m 1, int d 1, int y 1900 ); // constructor 14 15 void setDate( int, int, int ); // set the date Date &operator (); // preincrement operator 16 Date operator ( int ); 17 const Date &operator ( int ); // add days, modify object 18 bool leapYear( int ) const; // is this a leap year? 19 20 bool endOfMonth( int ) const; // is this end of month? // postincrement operator 21 private: 22 int month; 23 24 int day; int year; 25 26 static const int days[]; 27 void helpIncrement(); 28 }; 29 30 #endif // array of days per month // utility function
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 // Fig. 8.6: date1.cpp // Member function definitions for Date class #include iostream #include "date1.h" // Initialize static member at file scope; // one class-wide copy. const int Date::days[] { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // Date constructor Date::Date( int m, int d, int y ) { setDate( m, d, y ); } // Set the date void Date::setDate( int mm, int dd, int yy ) { month ( mm 1 && mm 12 ) ? mm : 1; year ( yy 1900 && yy 2100 ) ? yy : 1900; // test for a if ( month day ( dd else day ( dd leap year 2 && leapYear( year ) ) 1 && dd 29 ) ? dd : 1; 1 && dd days[ month ] ) ? dd : 1; } // Preincrement operator overloaded as a member function. Date &Date::operator () { helpIncrement(); return *this; // reference return to create an lvalue }
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 // Postincrement operator overloaded as a member function. // Note that the dummy integer parameter does not have a // parameter name. Date Date::operator ( int ) { postincrement operator Date temp *this; has a dummy int value. helpIncrement(); // return non-incremented, saved, temporary object return temp; // value return; not a reference return } // Add a specific number of days to a date const Date &Date::operator ( int additionalDays ) { for ( int i 0; i additionalDays; i ) helpIncrement(); return *this; // enables cascading } // If the year is a leap year, return true; // otherwise, return false bool Date::leapYear( int y ) const { if ( y % 400 0 ( y % 100 ! 0 && y % 4 0 ) ) return true; // a leap year else return false; // not a leap year } // Determine if the day is the end of the month bool Date::endOfMonth( int d ) const {
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
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;
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 d4 is d4 is d4 is the preincrement operator: March 18, 1969 March 19, 1969 March 19, 1969 Testing d4 is d4 is d4 is the postincrement operator: March 19, 1969 March 19, 1969 March 20, 1969