260 likes | 270 Views
Learn about the use of operator overloading in C++, including the "swap" function for different data types and complex number operations.
E N D
The ‘C’ swap() function • In ‘C’ we need a “swap” function for each different data type • void swapInt (int* a, int* b); • void swapDbl (double* a, double* b); • void swapBob (struct bob* a, struct bob* b) • etc., etc., etc. • Why??
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); • Is this an improvement? • Why can I do this in C++ and not C?
Function signatures • A function signature is what the compiler/linker uses to identify a function • In C, functions are identified only by their name • In C++, function signatures include name, parameters and, (for member functions) const --- NOT return type
Constructors are functions too Recall that we can create (declare) a file as an ifstream two ways --- • ifstream fileA; • ifstream fileB (“filename”); • These use different constructors • ifstream ( ); • ifstream (char * filename);
Operator Overloading Overview • Classes allow us to define our own data types in addition to the built in primitive types. • It’s often convenient for our classes to model the operations available on primitive types. • We can use the same concise notation for expressing how to manipulate our objects.
A Complex Class class Complex {public: Complex (int real = 0, int imagine = 0); int getReal ( void ) const; int getImagine (void ) const; void setReal (int n); void setImagine (int d);
Complex (cont’d) private: int real; int imagine; };
Using Complex Class • It makes sense to want to perform mathematical operations with Complex numbers Complex C1 (3, 5), C2 (5, 9), C3; C3 = C1 + C2; C2 = C3 * C1; C1 = -C2;
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+”
operator+ as a member function • What’s the full name of the function? • What should the return type be? • What are the parameters? (trick question) • Should it be const?
Declaring operator+ as a member function class Complex { public: const Complex operator+ (const Complex& rhs) const; ….. }; • Why just one parameter?????
Complex::operator+ const Complex Complex :: operator+ (const Complex & rhs) const { Complex sum; // accessor/mutators not required sum.imagine = imagine + rhs.imagine; // but preferred sum.setReal( getReal( ) + rhs.getReal ( ) ); return sum; }
Using operator+ • We can now write (cascading operators) C4 = C3 + C2 + C1; • We can also write (how does it work?) C3 = C2 + 7; • But C3 = 7 + C2 is a compiler error (Why?)
Operator+ as a non-member (ordinary old) function • What’s the full name of the function? • What should the return type be? • What are the parameters? • ‘const’ does not apply to non-member functions
operator+non-member, non-friend const Complex operator+ (const Complex& lhs, const Complex& rhs) { // must use accessors and mutators Complex sum; sum.setImagine (lhs.getImagine( ) + rhs.getImagine( ) ); sum.setReal (lhs.getReal ( ) + rhs.getReal( ) ); return sum; }
Declaring operator+as non-member friend • Declare operator+ as a friend in class definition class Complex { public: friend const Complex operator+ (const Complex& a, const Complex& b); …. };
Operator+ asnon-member friend 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; }
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. • What are the parameters to the operator<< function call when we write cout << C5; • What does this imply?
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 ‘ostream &’ • Why? • So we can write cout << C5 << “is a complex number” • This is really a set of nested function calls Operator<< (operator<<(cout, C5), “…..”);
Overloading unary operators Complex C1(4, 5), C2; C2 = -C1; Is an example of a unary operator (minus) We can and should overload as member
Unary operator- const Complex Complex :: operator- ( ) const { Complex x; x.real = -real; x.imagine = imagine; return x; }
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 associativity • You can change the number of operands
Good Programming Practices • Overloaded binary arithmetic operators should • Return const objects by value • Be written as non-member functions when appropriate to allow commutativity • Overload unary operators as members • Always overload operator<< • Overload operators so that they mimic the behavior of primitive data types