220 likes | 321 Views
Operator Overloading. Strong Suggestion : Go over the Array class example in Section 8.8 of your text. (You may ignore the Array copy constructor for now.). Review -- Function Signatures. A function signature is what the compiler and linker use to identify a function.
E N D
Operator Overloading Strong Suggestion: Go over the Array class example in Section 8.8 of your text. (You may ignore the Array copy constructor for now.)
Review -- Function Signatures • A function signature is what the compiler and linker use to identify a function. • In C, functions are identified only by their name. • In C++, a function’s signature includes its name, parameters, and (for member functions) const. It does NOT include the return type.
A C++ swap( ) Function • We still need separate functions, but they can all have the same name. • void swap (int& a, int& b); • void swap (double& a, double& b); • void swap (struct bob& a, struct bob& b);
Operator Overloading Overview • Many C++ operator are already overloaded for primitive types. Examples: + - * / << >> • It is often convenient for our classes to imitate the operations available on primitive types (e.g., + or - ). • Then we can use the same concise notation for manipulating our objects.
A Complex Number Class class Complex {public: Complex (int real = 0, int imagine = 0); int getReal ( ) const; int getImagine ( ) const; void setReal (int n); void setImagine (int d); private: int real; int imagine; };
Using Complex Class • It makes sense to want to perform mathematical operations with Complex objects. Complex C1 (3, 5), C2 (5, 9), C3; C3 = C1 + C2; // addition C2 = C3 * C1; // subtraction C1 = -C2; // negation
Operators Are Really Functions • For user-defined types, when you use an operator, you are making a function call. • Consider the expression: C2 + C1 • This is translated into a function call. • The name of the function is “operator+” • The call is: C2.operator+(C1);
Declaring operator+ As a Member Function class Complex { public: const Complex operator+ (const Complex &rhs) const; … }; • Note all of the const’s!
operator+ Implementation const Complex Complex :: operator+ (const Complex &rhs) const { Complex sum; // accessor and mutators not required sum.imagine = imagine + rhs.imagine; // but preferred sum.setReal( getReal( ) + rhs.getReal ( ) ); return sum; }
Using operator+ • We can now write C3 = C2 + C1; • We can also use cascading operators. C4 = C3 + C2 + C1; • And we can write C3 = C2 + 7; • But C3 = 7 + C2 is a compiler error. (Why?)
operator+ As aNon-member, Non-friend const Complex operator+ (const Complex &lhs, // extra parameter const Complex &rhs) // not const { // must use accessors and mutators Complex sum; sum.setImagine (lhs.getImagine( ) + rhs.getImagine( ) ); sum.setReal (lhs.getReal ( ) + rhs.getReal( ) ); return sum; } // is now commutative
Declaring operator+As a Non-member Friend • Declare operator+ as a friend in the class definition. class Complex { public: friend const Complex operator+ (const Complex& a, const Complex& b); … };
Operator+ As aNon-member Friend (con’t) const Complex operator+ (const Complex& lhs, const Complex& rhs) { Complex sum; // accessors and mutators not required sum.imagine = lhs.imagine + rhs.imagine; // but preferred sum.setReal( lhs.getReal( ) + rhs.getReal( )) ; return sum; } // violates encapsulation! Non-friend better.
Printing Objects • Each object should be responsible for printing itself. • This guarantees objects are always printed the same way. • It allows us to write intuitive output code: Complex C5 (5, 3); cout << C5 << endl;
Operator<< • The insertion operator << is a function and can (and should) be overloaded. We can do operator>>, too. • << is a binary operator. • The left-hand operand is of type ostream& • Therefore, operator<< cannot be a member function. It must be a non-member.
operator<< ostream& operator<< (ostream& out, const Complex& c) { out << c.getReal( ); int imagine = c.getImagine( ); out << (imagine < 0 ? “ - ” : “ + ” ) out << imagine << “i”; return out; } • Could be, and often, is a friend • Note: no endl
Operator<< Returns Type ‘ostream &’ • Why? So we can write statements such as cout << C5 << “is a complex number” OR cout << C3 << endl << C2 << endl; • << associates from left to right.
Overloading Unary Operators Complex C1(4, 5), C2; C2 = -C1; is an example of a unary operator (minus). • We can and should overload this operator as a member function.
Unary operator- const Complex Complex :: operator- ( ) const { Complex x; x.real = -real; x.imagine = imagine; return x; }
Overloading = • Remember that assignment performs a memberwise (shallow) copy by default. • This is not sufficient when a data member is dynamically allocated. • = must be overloaded to do a deep copy.
Restrictions • Not all operators can be overloaded. • You can’t make up your own operators. • You can’t overload operators for primitive types (like int). • You can’t change the precedence of an operator. • You can’t change the associativity of an operator.
Good Programming Practices • Overload operators so that they mimic the behavior of primitive data types. • Overloaded binary arithmetic operators should • return const objects by value • be written as non-member functions when appropriate to allow commutativity • be written as non-friend functions (if data member accessors are available) • Overload unary operators as member functions. • Always overload << • Always overload = for objects with dynamic data members.