Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about...

46
Operator Overloading Back to Fractions...

Transcript of Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about...

Page 1: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

Operator Overloading

Back to Fractions...

Page 2: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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.

Page 3: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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

Page 4: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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?

Page 5: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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?

Page 6: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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

Page 7: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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!

Page 8: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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!

Page 9: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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

Page 10: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

Implementing Our Object

• Step 1 (con't): more declarationsFraction add(const Fraction &f) const;Fraction subtract(const Fraction &f) const;Fraction multiply(const Fraction &f) const;Fraction divide(const Fraction &f) const; // comparison methodsint compare(const Fraction &f) const;bool equals(const Fraction &f) const; // conversion methodsint intValue() const;double doubleValue() const;string toString() const;

Page 11: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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 willNOT change the object on which

it is called

Page 12: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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 returningthe object itself rather than

a pointer to the object as before(no *)

Use of string means we must#include <string>

Page 13: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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 fractionwere in reduced form?

Solution: implement a privategcd() function in Fraction,

use it to reduce form

Page 14: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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

Page 15: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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

Note: this is a cheesy implementation

of the gcd function!

Remember to putprototype in .h fileas private, static!

Oh yeah! And be sureto #include <cstdlib>in Fraction.cpp file!

Page 16: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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);}

Page 17: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

Implementing Our Object

• Step 2: … subtraction, multiplication,...

Pretty straight-forward…

Fraction Fraction::subtract(const Fraction &f) const{ int num = this->numerator * f.denominator; num -= f.numerator * this->denominator; int dnm = f.denominator * denominator;

return Fraction(num, dnm);}

Page 18: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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);}

Page 19: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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);}

Page 20: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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));}

Page 21: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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);}

Page 22: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

Implementing Our Object

• Step 2: … and conversion to stringstring 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

Page 23: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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

Page 24: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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

Page 25: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

Testing Our Class

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

Page 26: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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;

Page 27: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

Testing Our Class

• Step 3: and equals: cout << g1.toString() << " equals " << g2.toString() << " is " << g1.equals(g2) << endl;

Return 0; // done for now!}

Page 28: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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

Page 29: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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?

Page 30: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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!

Page 31: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

Conversion from int

• Step 1: More declarationsclass Fraction { private: int numerator; int denominator; static int gcd(int n, int d);

public: Fraction(int, int); Fraction(); Fraction(int n); // conversion from int ...

Page 32: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

Conversion from int

• Step 2: implementation

Fraction::Fraction(int n){ numerator = n; denominator = 1;}

So what about floats?

Hmmmmmm....

Page 33: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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

Page 34: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

Overloading Operators

• Step 1: More declarationsclass 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)

Page 35: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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);}

Page 36: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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

Page 37: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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

Page 38: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

More Operators

• Step 1: More declarationsclass 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)

Page 39: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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));}

Page 40: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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?

Page 41: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

AA Operators

• Step 1: More declarationsclass 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!

Page 42: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

More Operators

• Step 2: implementation

Fraction& Fraction::operator+=(const Fraction& f){ *this = this->add(f); return *this;}

reference return type so it can be Lvalue

Modify object

Return modified object

Page 43: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

More Testing Overload

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

Page 44: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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

Page 45: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

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!

Page 46: Operator Overloading Back to Fractions.... Implementing an Object We’ve talked at length about object- orientation. – We’ve looked heavily at encapsulation.

Questions?

• Project 4:

• Implement overload operators for set and multiset:

+ (union), - (subtraction), +=, -=,

* (intersect), ^ (difference), *=, ^=,

== (equality),

< (proper subset), <= (subset)