1 / 46

Operator Overloading

Explore implementing a data structure for fractions in C++ with operator overloading, conversions, and object behavior. Learn constructors, destructors, getters, setters, and automatic type conversions.

tammym
Download Presentation

Operator Overloading

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Operator Overloading Back to Fractions...

  2. Implementing an Object • We’ve talked at length about object-orientation. • We’ve looked heavily at encapsulation and related concerns. • We’ve started to implement a data structure for fractions. • Data structures are special classes/objects designed to organize data within a program.

  3. Fractions • As noted earlier, C++ does not have a fraction or rational data type or object class • This does not mean that we can’t create a class that provides this functionality! • Let's do it! – Get out laptops and bring up the code we started a couple weeks ago

  4. Object Requirements • Our core idea: we want a class of objects that behave like fractions • What are some of the implications of this? • What must we store/track? • What operations should be possible? • What conversions? • What about operators?

  5. Object Requirements • Our core idea: we want a class of objects that behave like fractions • Fractions have a numerator and a denominator – we must store these • We can assign, compare, add, subtract, multiply, and divide fractions • It would be nice to convert a fraction to an int or a double. How about the reverse directions? • What about strings?

  6. Object Requirements • Our core idea: we want a class of objects that behave like fractions • Converting to int or double is easy. • Converting from int is easy • Converting from double... … not so much! • Converting to a string is easy... … from a string a bit harder

  7. Object Requirements • Our core idea: we want a class of objects that behave like fractions • What about binary operators like +, -? • We can overload these just like functions! • How about +=, -=, etc.? Yep! • How about =, <, >, etc.? Yep, those too!

  8. Object Requirements • We need constructors and destructors • We may also need getters and setters for numerator and denominator • We can also provide for “automatic” type conversion!

  9. Implementing Our Object • Step 1: determining the declarations • These go in the header (Fraction.h) file. class Fraction { private: int numerator; int denominator; public: Fraction(int, int); Fraction(); // more later

  10. Implementing Our Object • Step 1 (con't): more declarations Fraction add(const Fraction &f) const; Fraction subtract(const Fraction &f) const; Fraction multiply(const Fraction &f) const; Fraction divide(const Fraction &f) const; // comparison methods int compare(const Fraction &f) const; bool equals(const Fraction &f) const; // conversion methods int intValue() const; double doubleValue() const; string toString() const;

  11. Implementing Our Object • Step 1(con't): more declarations Fraction add(const Fraction &f) const; Fraction subtract(const Fraction &f) const; Fraction multiply(const Fraction &f) const; Fraction divide(const Fraction &f) const; int compare(const Fraction &f) const; bool equals(const Fraction &f) const; int intValue() const; double doubleValue() const; string toString() const; Note the use of const here - This means that the method will NOT change the object on which it is called

  12. Implementing Our Object • Step 1(con't): more declarations Fraction add(const Fraction &f) const; Fraction subtract(const Fraction &f) const; Fraction multiply(const Fraction &f) const; Fraction divide(const Fraction &f) const; int compare(const Fraction &f) const; bool equals(const Fraction &f) const; int intValue() const; double doubleValue() const; string toString() const; Note also that we are returning the object itself rather than a pointer to the object as before (no *) Use of string means we must #include <string>

  13. Implementing Our Object • Step 2: implementing the methods (goes in Fraction.cpp file). Fraction::Fraction() : numerator(0), denominator(1) {} Fraction::Fraction(int n, int d) { numerator = n; denominator = d; } Wouldn't it be nice if the fraction were in reduced form? Solution: implement a private gcd() function in Fraction, use it to reduce form

  14. Implementing Our Object • Revised version of constructor: Fraction::Fraction() : numerator(0), denominator(1) {} Fraction::Fraction(int n, int d) { int g = gcd(n, d); if (g > 1) { n /= g; d /= g; } numerator = n; denominator = d; }

  15. Implementing Our Object • What does gcd look like? int Fraction::gcd(int n, int d) { int n1 = abs(n); // want these positive int n2 = abs(d); int gcd = 1; for (int k = 1; k <= n1 && k <= n2; ++k) { if (n1 % k == 0 && n2 % k == 0) gcd = k; } return gcd; } Remember to put prototype in .h file as private, static! Oh yeah! And be sure to #include <cstdlib> in Fraction.cpp file! Note: this is a cheesy implementation of the gcd function!

  16. Implementing Our Object • Step 2: OK – constructors done, now for addition, etc. (we did this one before, 'member?) Fraction Fraction::add(const Fraction &f) const { int num = this->numerator * f.denominator; num += f.numerator * this->denominator; int dnm = f.denominator * denominator; return Fraction(num, dnm); }

  17. Implementing Our Object • Step 2: … subtraction, multiplication,... Pretty straight-forward… Fraction Fraction::subtract(const Fraction &f) const { intnum = this->numerator * f.denominator; num-=f.numerator * this->denominator; intdnm = f.denominator * denominator; return Fraction(num, dnm); }

  18. Implementing Our Object • Step 2: … multiplication, division, ... Fraction Fraction::multiply(const Fraction &f) const { int num = this->numerator * f.numerator; int dnm = this->denominator * f.denominator; return Fraction(num, dnm); }

  19. Implementing Our Object • Step 2: … division,... Fraction Fraction::divide(const Fraction &f) const { // divide is multiply by reciprocal int num = this->numerator * f.denominator; int dnm = this->denominator * f.numerator; return Fraction(num, dnm); }

  20. Implementing Our Object • Step 2: Now for comparison... int Fraction::compare(const Fraction &f) const { Fraction temp = subtract(f); // difference int num = temp.getNum(); if (num < 0) return -1; // neg => smaller return (num > 0 ? 1 : 0); // pos => bigger } bool Fraction::equals(const Fraction &f) const { return(0 == compare(f)); }

  21. Implementing Our Object • Step 2: Conversion to built-ins ... • Easy peasy int Fraction::intValue() const { return (numerator)/(denominator); } double Fraction::doubleValue() const { return ((double) numerator )/((double) denominator); }

  22. Implementing Our Object • Step 2: … and conversion to string string Fraction::toString() const { stringstream ss; ss << numerator; if (denominator > 1 && numerator != 0) ss << "/" << denominator; return (ss.str()); } What is the if for??? Prevent output like 3/1 and 0/4

  23. Testing Our Class • Step 3: Writing some test code (This goes in testFraction.cpp) #include <iostream> #include <string> #include "Fraction.h" using namespace std; int main() { int i; int j; Fraction g1; ...

  24. Testing Our Class • Step 3: Writing test code ... Fraction g1; cout << "Enter two integers: " << flush; cin >> i >> j; g1.setNum(i); // test setters g1.setDenom(j); cout << "Enter two integers: " << flush; cin >> i >> j; Fraction g2(i,j); // test list constructor ...

  25. Testing Our Class • Step 3: Writing test code cout << g1.toString() // test toString << " plus " << g2.toString() << " equals " << g1.add(g2).toString() // test add << endl;

  26. Testing Our Class • Step 3: etc. etc. etc. for other 3 fcns, then do compare: cout << g1.toString() << " compare " << g2.toString() << " is " << g1.compare(g2) << endl;

  27. Testing Our Class • Step 3: and equals: cout << g1.toString() << " equals " << g2.toString() << " is " << g1.equals(g2) << endl; Return 0; // done for now! }

  28. Exercise 1: • Implement Fraction class (you should have most of this from before) – just constructors, setters, add(), toString(), and compare() for now... • Use direct form rather than pointer version • Compile – no need to have these as 3 files for now... check #includes!!! • Run test and see that it works

  29. Review Requirements • We have met all the basic requirements • Arithmetic operations, comparison, conversion to float, int, and string • Missing conversion from int, float, or string • What about operators? • And automatic conversion?

  30. Conversion from int • Automatic (compiler) conversion from other types can occur when a function is called (such as add) that needs another Fraction, but an int or float is the actual parameter • Compiler will first search for overloaded function with matching signature – not there! • Then it looks for … constructor!

  31. Conversion from int • Step 1: More declarations class Fraction { private: int numerator; int denominator; static int gcd(int n, int d); public: Fraction(int, int); Fraction(); Fraction(int n); // conversion from int ...

  32. Conversion from int • Step 2: implementation Fraction::Fraction(int n) { numerator = n; denominator = 1; } So what about floats? Hmmmmmm....

  33. Overloading Operators • Wouldn't it be nice to be able to use code like if (f1 > f2) { … } • Well, C++ allows this! • In fact, C++ allows lots of operators to be overloaded • Use the special operator function • Named with operator keyword followed by the actual operator

  34. Overloading Operators • Step 1: More declarations class Fraction { private: int numerator; int denominator; static int gcd(int n, int d); public: ... bool operator<(const Fraction& f) const; bool operator==(const Fraction& f) const; ... operator keyword Operator symbol(s)

  35. Overloading Operators • Step 2: implementation bool Fraction::operator<(const Fraction& f) const { return (compare(f) < 0); } bool Fraction::operator==(const Fraction& f) const { return (compare(f) == 0); }

  36. Testing Operator Overload • Step 3: Writing some test code ... // test overloading < operator cout << g1.toString() << " < " << g2.toString() << " is " << (g1 < g2) << endl; // now test auto conversion from int cout << g1.toString() << " plus 1 equals " << g1.add(1).toString() << endl; ...

  37. Exercise 2: • Add declarations and implementation for integer conversion and overloading < operator • Add a little code to test these • Compile • Run test and see that it works

  38. More Operators • Step 1: More declarations class Fraction { ... public: ... Fraction operator+(const Fraction& f) const; Fraction operator-(const Fraction& f) const; Fraction operator*(const Fraction& f) const; Fraction operator/(const Fraction& f) const; ... operator keyword Operator symbol(s)

  39. More Operators • Step 2: implementation Fraction Fraction::operator+(const Fraction& f) const { return (add(f)); } Fraction Fraction::operator-(const Fraction& f) const { return (subtract(f)); }

  40. Augmented Assignment • What about code like f1 += f2; • Well, C++ also allows this! • Issue here is that assignment also returns a Lvalue... how to solve? • So declare as reference Fraction& and return object • Can't use const any more!!! Why not?

  41. AA Operators • Step 1: More declarations class Fraction { ... public: ... Fraction& operator+=(const Fraction& f) ; Fraction& operator-=(const Fraction& f) ; Fraction& operator*=(const Fraction& f) ; Fraction& operator/=(const Fraction& f) ; ... reference type return value No more const!

  42. More Operators • Step 2: implementation Fraction& Fraction::operator+=(const Fraction& f) { *this = this->add(f); return *this; } Modify object Return modified object reference return type so it can be Lvalue

  43. More Testing Overload • Step 3: Writing some test code ... cout << g1.toString() << " plus equal " << g2.toString() << " equals "; g1 += g2; cout << g1.toString() << endl; ...

  44. Exercise 3: • Add declarations and implementation for overloading + and += operators • Add a little code to test these • Compile • Run test and see that it works

  45. Operator Overloading • Can also overload [] indexing operator – see lab • Only operators that can't be overloaded in C++ are: ?: . .* :: • Only overload operators when the overloaded function fulfills the logical function of the original operator!

  46. Questions? • Project 4: • Implement overload operators for set and multiset: + (union), - (subtraction), +=, -=, * (intersect), ^ (difference), *=, ^=, == (equality), < (proper subset), <= (subset)

More Related