Operator Overloading in C++: Exploring New Versions of Existing Operators

Operator Overloading in C++: Exploring New Versions of Existing Operators
Slide Note
Embed
Share

An in-depth look at operator overloading in C++, allowing programmers to redefine existing operators for built-in types like int, double, etc. Understand the rules, motivations, and differences between friend and member functions. Explore the format for overloading operators and how it impacts the meaning without altering the grammar.

  • C++
  • Operator Overloading
  • Programming
  • Object-Oriented
  • Functions

Uploaded on Feb 22, 2025 | 0 Views


Download Presentation

Please find below an Image/Link to download the presentation.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.

You are allowed to download the files provided on this website for personal or commercial use, subject to the condition that they are used lawfully. All files are the property of their respective owners.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.

E N D

Presentation Transcript


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

  2. Fundamentals Many existing operators that work on built-in types (e.g., int, double) Operator overloading Operator overloading allows programmers to define new versions of these operators A C++ operator 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

  3. 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

  4. 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

  5. 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 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 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

  6. 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 operator and the operator symbol operator+ operator++ operator<< operator== Format of operator overload declaration return_type operator<operator_symbol> (parameter_list);

  7. 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 n1, n2, n3; n3 = n1 + n2;

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

  9. 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

  10. 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 f1, Fraction f2); To add, perform the following Fraction n1, n2, n3; n3 = Add(n1, n2);

  11. The + Operator With operator overloading, we can have the following friend Fraction operator+(Fraction f1, Fraction f2); Or the following friend Fraction operator+(const Fraction &f1, const Fraction &f2); Thus, we can invoke the function this way n3 = operator+(n1, n2); Or, we can use the infix infix notation n3 = n1 + n2; // cascading also works (n1 + n2 + n3 + n4 ) Note: const is only allowed for member functions

  12. Definition of the + Operator Fraction operator+(Fraction f1, Fraction f2) { Fraction r; r.numerator = (f1.numerator*f2.denominator) + (f2.numerator*f1.denominator); r.denominator = f1.denominator*f2.denominator; return r; }

  13. Code Example http://www.cs.fsu.edu/~myers/cop3330/examples/oo/frac1 /

  14. frac.h class Fraction { friend Fraction operator+(const Fraction &f1, public: private: }; const Fraction &f2);

  15. frac.cpp Fraction operator+(const Fraction &f1, const Fraction &f2) { Fraction r; r.numerator = (f1.numerator*f2.denominator) + (f2.numerator*f1.denominator); r.denominator = f1.denominator*f2.denominator; return r; }

  16. main.cpp #include <iostream> #include frac.h using namespace std; Int main() { Fraction f1, f2, f3(3,4), f4(6); cout << \n Now enter first fraction: ; f1.Input(); cout << \nYou entered ; f1.Show(); cout << \n Now enter first fraction: ; f2.Input(); cout << \nYou entered ; f2.Show();

  17. main.cpp cout << \nThe sum of the first and second fraction is ; Fraction result; result = f1 + f2; result.Show(); cout << \n ; } cout << Attempting arithmetic calls\n ; f2 = f1 + 5; cout << Second arithmetic call\n ; f4 = 2 + f3; cout << \n The fraction f1 is ; f1.Show();

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

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

  20. Example Code http://www.cs.fsu.edu/~myers/cop3330/examples/oo/frac2 /

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

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

  23. main.cpp #include <iostream> #include frac.h using namespace std; Int main() { Fraction f1, f2, f3(3,4), f4(6); cout << \n Now enter first fraction: ; f1.Input(); cout << \nYou entered ; f1.Show(); cout << \n Now enter first fraction: ; f2.Input(); cout << \nYou entered ; f2.Show();

  24. main.cpp cout << \nThe sum of the first and second fraction is ; Fraction result; result = f1 + f2; result.Show(); cout << \n ; } cout << Attempting arithmetic calls\n ; f2 = f1 + 5; cout << Second arithmetic call\n ; f4 = 2 + f3; // won t work with member function f4 = 2 + f3; // won t work with member function cout << \n The fraction f1 is ; f1.Show();

  25. Other Arithmetic Operators Multiplication overload friend Fraction operator*(Fraction f1, Fraction f2); 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

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

  27. Overloading Comparison Operators Can be overloaded either as stand-alone or member functions Here is the original Equals function friend bool Equals(const Fraction &f1, const Fraction &f2); We can write this as an operator overload friend bool operator==(const Fraction &f1, const Fraction &f2); Here are the corresponding calls Fraction n1, n2; if (Equals(n1, n2)) cout << n1 and n2 are equal ; If (n1 == n2) cout << n1 and n2 are equal ; Hmm Can apple == orange? Well, if we see them as fruits

  28. Can Overload All Six operator== operator!= operator< operator> operator<= operator>=

  29. 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

  30. 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);

  31. 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

  32. 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) { s << f.numerator << / << f.denominator; return s; } Use s, not cout Need a return type

  33. Using the Insertion << and Extraction >> Operators Member function Overloaded operator Fraction f1; Fraction f1; cout << f1 is ; f1.Show(); cout << \n ; cout << f1 is << f1 << \n ; // same as (((cout << f1 is ) << f1) << \n );

  34. Example with << Overload http://www.cs.fsu.edu/~myers/cop3330/examples/oo/frac3 / How about >> overload for Fraction? Try it!

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

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

  37. main.cpp #include <iostream> #include frac.h using namespace std; int main () { Fraction f1, f2, f3(3,4), f4(6); cout << \n The fraction f1 is << f1 << \n ; cout << \n The fraction f2 is << f2; cout << \n The fraction f3 is << f3; cout << \n The fraction f4 is << f4;

  38. main.cpp cout << \n Now enter first fraction: ; f1.Input(); cout << \nYou entered << f1; cout << \n Now enter second fraction: ; f2.Input(); cout << \nYou entered << f2; Fraction result; result = f1 + f2; cout << \nThe sum of the first and second fraction is << result << \n ;

  39. main.cpp cout << \n The value of fraction 1 is << f1.Evaluate() << \n ; \n ; cout << \n The value of fraction 2 is << f2.Evaluate() << } cout << Goodbyte!\n ; return 0;

  40. Another Example http://www.cs.fsu.edu/~myers/cop3330/examples/complex / Complex numbers i2 = -1 Arithmetic operators: +, -, *, / Insertion and extraction operators: <<, >> Increment and decrement: ++, --

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

  42. 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)/(c2 + d2) = (ac + bd)/(c2 + d2) + (bc ad)i/(c2 + d2) conjugate

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

  44. 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 + 0i) // conversion constructor (double to complex // constructor with 2 parameters (r + im*i) ~Complex(); double getReal() const; double getImaginary() const; void set(double r, double im = 0.0);

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

  46. complex.cpp #include complex.h // iostream is already in complex.h Complex operator+(const Complex &c1, const Complex & c2) { return Complex(c1.real + c2.real, c1.imag + c2.imag); } Complex operator-(const Complex &c1, const Complex &c2) { return Complex(c1.real c2.real, c1.imag c2.image); }

  47. complex.cpp Complex operator*(const Complex &c1, const Complex &c2) { double realPart = c1.real*c2.real c1.imag*c2.imag; double imagPart = c1.imag*c2.real + c1.real*c2.imag; return Complex(realPart, imagPart); } Complex operator/(const Complex &c1, const Complex &c2) { double realPart = (c1.real*c2.real + c1.imag*c2.imag) / (c2.real*c2.real + c2.imag*c2.imag); double imagPart = (c1.imag*c2.real c1.real*c2.imag) / (c2.real*c2.real + c2.imag*c2.imag); return Complex(realPart, imagPart); }

  48. 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

  49. complex.cpp // input format: <real> + <imag>i istream &operator>>(istream &is, Complex &c) { char symbol, iMarker; is >> c.real >> symbol >> c.imag >> iMarker; if (symbol == ) c.imag = -c.imag; return is; } Complex::Complex(double r, double im) { real = r; imag = im; } Complex::~Complex(); double Complex::getReal() const { return real; } double Complex::getImaginary() const { return imag; } void Complex::set(double r, double im) { real = r; imag = im; }

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

Related


More Related Content