OPERATOR OVERLOADING Andy Wang Object Oriented Programming in

  • Slides: 52
Download presentation
OPERATOR OVERLOADING Andy Wang Object Oriented Programming in C++ COP 3330

OPERATOR OVERLOADING Andy Wang Object Oriented Programming in C++ COP 3330

Fundamentals ■ Many existing operators that work on built-in types (e. g. , int,

Fundamentals ■ Many existing operators that work on built-in types (e. g. , int, double) ■ Operator overloading allows programmers to define new versions of these operators – A C++ operator is just a function called with special notation ■ Overloading a function involves writing a function – C++ already has operator overloading ■ ■ + operator works on ints, floats, doubles, and chars Different versions of + exist for each type

Some Rules Regarding Operator Overloading ■ Overloading an operator – Cannot change a number

Some Rules Regarding Operator Overloading ■ Overloading an operator – Cannot change a number of properties ■ ■ ■ Precedence – a + b*c Associativity – (a + b) + c = a + (b + c) Number of operands – Cannot create new operators – Can only overload the existing ones ■ Operator meaning on the built-in types cannot be changed

Some Rules Regarding Operator Overloading ■ In other words – You can change the

Some Rules Regarding Operator Overloading ■ In other words – You can change the meaning, not the grammar ■ ■ That is “sick” –Big Hero 6 Yes, you can define ‘+’ as subtraction

Friend vs. Member Functions ■ Some operators can be written as member functions ■

Friend vs. Member Functions ■ Some operators can be written as member functions ■ Some operators can be written as stand-alone friend functions ■ A binary operator has two operands – As a stand-alone function, it will take two parameters – As a member function, the first operand is the calling object ■ Taking the second operand as a parameter How about “-”? ■ A unary operator has one operand – As a stand-alone function, it will take a parameter – As a member function, one calling object with no parameters

Format ■ An operator is just a function – It requires a name, a

Format ■ An operator is just a function – It requires a name, a return type, and a parameter list – Name of an operator is always a conjunction of the keyword operator and the operator symbol ■ ■ operator++ operator<< operator== – Format of operator overload declaration ■ return_type operator<operator_symbol> (parameter_list);

Motivation ■ Example 1 – Arithmetic operators int x = 3, y = 6,

Motivation ■ Example 1 – Arithmetic operators int x = 3, y = 6, z; float a = 3. 4, b = 2. 1, c; z = x + y; c = a / b + 1 / 3; – Can we use arithmetic operators on our Fraction class (a user-defined type)? Fraction n 1, n 2, n 3; n 3 = n 1 + n 2;

Example 1 – The answer is NO ■ ■ Since Fraction is a user-defined

Example 1 – The answer is NO ■ ■ Since Fraction is a user-defined type Operator overloading makes this possible

Example 2 ■ The following screen output is legal int x = 5, y

Example 2 ■ The following screen output is legal int x = 5, y = 0; cout << x << y; ■ How about this? Fraction f(3, 4); cout << “The fraction f = “ << f << ‘n’; ■ No, since the iostream library does not know about Fraction objects ■ Again, operator overloading can help here

Overloading the Arithmetic Operators ■ Can be overloaded either as stand-alone functions or as

Overloading the Arithmetic Operators ■ Can be overloaded either as stand-alone functions or as member functions ■ Originally, we have the following friend Fraction Add(Fraction f 1, Fraction f 2); – To add, perform the following Fraction n 1, n 2, n 3; n 3 = Add(n 1, n 2);

The + Operator ■ With operator overloading, we can have the following friend Fraction

The + Operator ■ With operator overloading, we can have the following friend Fraction operator+(Fraction f 1, Fraction f 2); ■ Or the following friend Fraction operator+(const Fraction &f 1, const Fraction &f 2); ■ Thus, we can invoke the function this way n 3 = operator+(n 1, n 2); Note: const is only allowed for member functions ■ Or, we can use the infix notation n 3 = n 1 + n 2; // cascading also works (n 1 + n 2 + n 3 + n 4…)

Definition of the + Operator Fraction operator+(Fraction f 1, Fraction f 2) { Fraction

Definition of the + Operator Fraction operator+(Fraction f 1, Fraction f 2) { Fraction r; r. numerator = (f 1. numerator*f 2. denominator) + (f 2. numerator*f 1. denominator); r. denominator = f 1. denominator*f 2. denominator; return r; }

Code Example ■ http: //www. cs. fsu. edu/~myers/cop 3330/examples/oo/frac 1/

Code Example ■ http: //www. cs. fsu. edu/~myers/cop 3330/examples/oo/frac 1/

frac. h class Fraction { friend Fraction operator+(const Fraction &f 1, const Fraction &f

frac. h class Fraction { friend Fraction operator+(const Fraction &f 1, const Fraction &f 2); public: … private: … };

frac. cpp … Fraction operator+(const Fraction &f 1, const Fraction &f 2) { Fraction

frac. cpp … Fraction operator+(const Fraction &f 1, const Fraction &f 2) { Fraction r; r. numerator = (f 1. numerator*f 2. denominator) + (f 2. numerator*f 1. denominator); r. denominator = f 1. denominator*f 2. denominator; return r; } …

main. cpp #include <iostream> #include “frac. h” using namespace std; Int main() { Fraction

main. cpp #include <iostream> #include “frac. h” using namespace std; Int main() { Fraction f 1, f 2, f 3(3, 4), f 4(6); … cout << “n Now enter first fraction: “; f 1. Input(); cout << “n. You entered “; f 1. Show(); cout << “n Now enter first fraction: “; f 2. Input(); cout << “n. You entered “; f 2. Show();

main. cpp cout << “n. The sum of the first and second fraction is

main. cpp cout << “n. The sum of the first and second fraction is “; Fraction result; result = f 1 + f 2; result. Show(); cout << ‘n’; cout << “Attempting arithmetic callsn”; f 2 = f 1 + 5; cout << “Second arithmetic calln”; f 4 = 2 + f 3; cout << “n The fraction f 1 is “; f 1. Show(); … }

Overloading an Operator as a Member Function ■ Member function version of Add Fraction

Overloading an Operator as a Member Function ■ Member function version of Add Fraction Add(const Fraction &f) const; ■ To call this function Fraction n 1, n 2, n 3; n 3 = n 1. Add(n 2); ■ To overload +, we can change the name Add to operator+ Fraction operator+(const Fraction &f) const; ■ To call, either way will work n 3 = n 1. operator+(n 2); // hardly anyone will do this n 3 = n 1 + n 2; // n 1 is the calling object, n 2 is the // parameter

Definition of operator+ Fraction: : operator+(const Fraction &f) const { Fraction r; r. numerator

Definition of operator+ Fraction: : operator+(const Fraction &f) const { Fraction r; r. numerator = (numerator*f. denominator) + (f. numerator*denominator); r. denominator = (denominator*f. denominator); return r; }

Example Code ■ http: //www. cs. fsu. edu/~myers/cop 3330/examples/oo/frac 2/

Example Code ■ http: //www. cs. fsu. edu/~myers/cop 3330/examples/oo/frac 2/

frac. h class Fraction { public: Fraction operator+(const Fraction &f) const; … private: …

frac. h class Fraction { public: Fraction operator+(const Fraction &f) const; … private: … };

frac. cpp … Fraction: : operator+(const Fraction &f) const { Fraction r; r. numerator

frac. cpp … Fraction: : operator+(const Fraction &f) const { Fraction r; r. numerator = (numerator*f. denominator) + (f. numerator*denominator); r. denominator = denominator*f. denominator; return r; } …

main. cpp #include <iostream> #include “frac. h” using namespace std; Int main() { Fraction

main. cpp #include <iostream> #include “frac. h” using namespace std; Int main() { Fraction f 1, f 2, f 3(3, 4), f 4(6); … cout << “n Now enter first fraction: “; f 1. Input(); cout << “n. You entered “; f 1. Show(); cout << “n Now enter first fraction: “; f 2. Input(); cout << “n. You entered “; f 2. Show();

main. cpp cout << “n. The sum of the first and second fraction is

main. cpp cout << “n. The sum of the first and second fraction is “; Fraction result; result = f 1 + f 2; result. Show(); cout << ‘n’; cout << “Attempting arithmetic callsn”; f 2 = f 1 + 5; cout << “Second arithmetic calln”; f 4 = 2 + f 3; // won’t work with member function cout << “n The fraction f 1 is “; f 1. Show(); … }

Other Arithmetic Operators ■ Multiplication overload friend Fraction operator*(Fraction f 1, Fraction f 2);

Other Arithmetic Operators ■ Multiplication overload friend Fraction operator*(Fraction f 1, Fraction f 2); ■ Operator to add a Fraction and an integer friend Fraction operator+(Fraction f, int n); // not needed ■ Operator to add an integer to a Fraction friend Fraction operator+(int n, Fraction f); // not needed ■ The last two operators are not needed, since we have conversion constructors

Friend vs. Member Operator Overloading ■ Will both cases work for friend version and

Friend vs. Member Operator Overloading ■ Will both cases work for friend version and the member function version? Fraction n 1, n 2, n 3; n 3 = n 1 + 5; // case 1 n 3 = 10 + n 2; // case 2 ■ What is the type of the calling object? – Does it have the necessary conversion constructors?

Overloading Comparison Operators ■ Can be overloaded either as stand-alone or member functions ■

Overloading Comparison Operators ■ Can be overloaded either as stand-alone or member functions ■ Here is the original Equals function friend bool Equals(const Fraction &f 1, const Fraction &f 2); ■ We can write this as an operator overload friend bool operator==(const Fraction &f 1, const Fraction &f 2); ■ Here are the corresponding calls Fraction n 1, n 2; if (Equals(n 1, n 2)) cout << “n 1 and n 2 are equal”; If (n 1 == n 2) cout << “n 1 and n 2 are equal”; Hmm… Can apple == orange? Well, if we see them as fruits…

Can Overload All Six ■ operator== ■ operator!= ■ operator< ■ operator> ■ operator<=

Can Overload All Six ■ operator== ■ operator!= ■ operator< ■ operator> ■ operator<= ■ operator>=

Overloading the Insertion << and Extraction >> Operators ■ << and >> are defined

Overloading the Insertion << and Extraction >> Operators ■ << and >> are defined for basic types – Thus, the following code should not work Fraction f; cout << f; ■ You need to teach the computer to work user-defined types

Overloading the Insertion << and Extraction >> Operators ■ << and >> are binary

Overloading the Insertion << and Extraction >> Operators ■ << and >> are binary operators – The first operator is always an io stream object (e. g. , cout) ■ – Thus, << cannot be defined as a member function Should be defined as outside (usually friend) functions ■ The second parameter is the user-defined type ■ Here is the overloading function friend ostream& operator<<(ostream &s, Fraction f); – Or a better version friend ostream& operator<<(ostream &s, const Fraction &f);

Overloading the Insertion << and Extraction >> Operators ■ Here is the corresponding declaration

Overloading the Insertion << and Extraction >> Operators ■ Here is the corresponding declaration for >> friend istream& operator>>(istream &s, Fraction &f); – Notice that f is passed by reference ■ ■ Need to modify the original No const

Defining the Insertion << and Extraction >> Operators ■ Recall the Show() member function

Defining the Insertion << and Extraction >> Operators ■ Recall the Show() member function void Fraction: : Show() { cout << numerator << ‘/’ << denominator; } ■ Here is the << operator friend function for Fraction ostream& operator<<(ostream &s, const Fraction &f) { Need a s << f. numerator << ‘/’ << f. denominator; return s; type } Use s, not cout

Using the Insertion << and Extraction >> Operators Member function Overloaded operator Fraction f

Using the Insertion << and Extraction >> Operators Member function Overloaded operator Fraction f 1; cout << “f 1 is “; f 1. Show(); cout << ‘n’; cout << “f 1 is “ << f 1 << ‘n’; // same as (((cout << “f 1 is “) << f 1) << ‘n’);

Example with << Overload ■ http: //www. cs. fsu. edu/~myers/cop 3330/examples/oo/frac 3/ ■ How

Example with << Overload ■ http: //www. cs. fsu. edu/~myers/cop 3330/examples/oo/frac 3/ ■ How about >> overload for Fraction? – Try it!

frac. h class Fraction { … friend ostream &operator<<(ostream &s, const Fraction &f); public:

frac. h class Fraction { … friend ostream &operator<<(ostream &s, const Fraction &f); public: … private: … };

frac. cpp … ostream& operator<<(ostream &s, const Fraction &f) { s << f. numerator

frac. cpp … ostream& operator<<(ostream &s, const Fraction &f) { s << f. numerator << ‘/’ << f. denominator; return s; } …

main. cpp #include <iostream> #include “frac. h” using namespace std; int main () {

main. cpp #include <iostream> #include “frac. h” using namespace std; int main () { Fraction f 1, f 2, f 3(3, 4), f 4(6); cout << “n The fraction f 1 is “ << f 1 << ‘n’; cout << “n The fraction f 2 is “ << f 2; cout << “n The fraction f 3 is “ << f 3; cout << “n The fraction f 4 is “ << f 4;

main. cpp cout << “n Now enter first fraction: “; f 1. Input(); cout

main. cpp cout << “n Now enter first fraction: “; f 1. Input(); cout << “n. You entered “ << f 1; cout << “n Now enter second fraction: “; f 2. Input(); cout << “n. You entered “ << f 2; Fraction result; result = f 1 + f 2; cout << “n. The sum of the first and second fraction is “ << result << ‘n’;

main. cpp cout << “n The value of fraction 1 is “ << f

main. cpp cout << “n The value of fraction 1 is “ << f 1. Evaluate() << ‘n’; cout << “n The value of fraction 2 is “ << f 2. Evaluate() << ‘n’; cout << “Goodbyte!n”; return 0; }

Another Example ■ http: //www. cs. fsu. edu/~myers/cop 3330/examples/complex/ – Complex numbers – i

Another Example ■ http: //www. cs. fsu. edu/~myers/cop 3330/examples/complex/ – Complex numbers – i 2 = -1 ■ ■ ■ Arithmetic operators: +, -, *, / Insertion and extraction operators: <<, >> Increment and decrement: ++, --

Prefix vs. Postfix Notation Prefix Postfix int j = 0; while (j < 10)

Prefix vs. Postfix Notation Prefix Postfix int j = 0; while (j < 10) { cout << ++j << endl; } // same as while (j < 10) { j = j + 1; cout << j << endl; } Int j = 0; while (j < 10) { cout << j++ << endl; } // same as while (j < 10) { cout << j << endl; j = j + 1; }

Review of Complex Numbers ■ (a + bi) + (c + di) = (a

Review of Complex Numbers ■ (a + bi) + (c + di) = (a + c) + (b + d)i ■ (a + bi) – (c + di) = (a – c) + (b – d)i ■ (a + bi)(c + di) = ac + adi + bci – bd = (ac – bd) + (ad + bc)i ■ (a + bi)/(c + di) = [(a + bi)/(c + di)][(c – di)/c – di)] = (ac – adi + bci + bd)/(c 2 + d 2) = (ac + bd)/(c 2 + d 2) + (bc – ad)i/(c 2 + d 2) conjugate

complex. h #include <iostream> using namespace std; class Complex { friend Complex operator+(const Complex

complex. h #include <iostream> using namespace std; class Complex { friend Complex operator+(const Complex &, const Complex &); friend Complex operator-(const Complex &, const Complex &); friend Complex operator*(const Complex &, const Complex &); friend Complex operator/(const Complex &, const Complex &);

complex. h Notice the return types friend ostream &operator<<(ostream &, const Complex &); friend

complex. h Notice the return types friend ostream &operator<<(ostream &, const Complex &); friend istream &operator>>(istream &, Complex &); public: Complex(double r = 0. 0, double im = 0. 0) // default constructor (sets to 0 + 0 i) // conversion constructor (double to complex // constructor with 2 parameters (r + im*i) ~Complex(); double get. Real() const; double get. Imaginary() const; void set(double r, double im = 0. 0);

complex. h Complex& operator++(); // prefix increment Complex operator++(int); // postfix increment Complex& operator--();

complex. h Complex& operator++(); // prefix increment Complex operator++(int); // postfix increment Complex& operator--(); // prefix decrement Complex operator--(int); // postfix decrement private: double real, imag; }; Just a note to the compiler to indicate that it is a postfix operator

complex. cpp #include “complex. h” // iostream is already in complex. h Complex operator+(const

complex. cpp #include “complex. h” // iostream is already in complex. h Complex operator+(const Complex &c 1, const Complex & c 2) { return Complex(c 1. real + c 2. real, c 1. imag + c 2. imag); } Complex operator-(const Complex &c 1, const Complex &c 2) { return Complex(c 1. real – c 2. real, c 1. imag – c 2. image); }

complex. cpp Complex operator*(const Complex &c 1, const Complex &c 2) { double real.

complex. cpp Complex operator*(const Complex &c 1, const Complex &c 2) { double real. Part = c 1. real*c 2. real – c 1. imag*c 2. imag; double imag. Part = c 1. imag*c 2. real + c 1. real*c 2. imag; return Complex(real. Part, imag. Part); } Complex operator/(const Complex &c 1, const Complex &c 2) { double real. Part = (c 1. real*c 2. real + c 1. imag*c 2. imag) / (c 2. real*c 2. real + c 2. imag*c 2. imag); double imag. Part = (c 1. imag*c 2. real – c 1. real*c 2. imag) / (c 2. real*c 2. real + c 2. imag*c 2. imag); return Complex(real. Part, imag. Part); }

complex. cpp ostream &operator<<(ostream& os, const Complex &c) { if (c. imag = 0)

complex. cpp ostream &operator<<(ostream& os, const Complex &c) { if (c. imag = 0) return (os << c. real); if (c. real = 0) return (os << c. imag << ‘i’); os << c. real; if (c. imag > 0) os << “ + “ << c. imag << ‘i’; else os << “ – “ << -c. imag << ‘i’; return os; } // (cout << a << b << c) is the same as // (((cout << a) << b) << c) or // (((cout. operator<<(a)). operator<<(b)). operator<<(c)) // cout. operator<<(a) must return an object

complex. cpp // input format: <real> + <imag>i istream &operator>>(istream &is, Complex &c) {

complex. cpp // input format: <real> + <imag>i istream &operator>>(istream &is, Complex &c) { char symbol, i. Marker; is >> c. real >> symbol >> c. imag >> i. Marker; if (symbol == ‘ – ‘) c. imag = -c. imag; return is; } Complex: : Complex(double r, double im) { real = r; imag = im; } Complex: : ~Complex(); double Complex: : get. Real() const { return real; } double Complex: : get. Imaginary() const { return imag; } void Complex: : set(double r, double im) { real = r; imag = im; }

complex. cpp // prefix increment Complex &Complex: : operator++() { real++; return *this; }

complex. cpp // prefix increment Complex &Complex: : operator++() { real++; return *this; } // postfix increment Complex: : operator++(int) { Complex temp = *this; real++; return temp; // return OLD value } // prefix decrement Complex &Complex: : operator--() { real--; return *this; } // postfix decrement Complex: : operator--(int) { Complex temp = *this; real--; return temp; // return OLD value }

samplemain. cpp #include “complex. h” using namespace std; void Print. Complex(const char* label, Complex

samplemain. cpp #include “complex. h” using namespace std; void Print. Complex(const char* label, Complex c) { cout << label << “: “ << c << ‘n’; } int main() { Complex c 1, c 2(7. 5), c 3(3. 6, 2. 1), c 4(5, -8), c 5(0, 8. 4), c 6(0, -9. 3); Print. Complex(“c 1”, c 1); … Print. Complex(“c 6, c 6);

samplemain. cpp cout << “Enter new value for c 1: “; cin >> c

samplemain. cpp cout << “Enter new value for c 1: “; cin >> c 1; cout << “Enter new value for c 2: “; cin >> c 2; cout << “n. You entered: n”; Print. Complex(“c 1”, c 1); Print. Complex(“c 2”, c 2); }