Topic: Operator Overloading (Operator Polymorphism) Using Fraction Class

As taught in the lecture class, C++ allows operator overloading for all mathematical operators and most other. The purpose of this software lab is to write Fraction and FractionList classes that simplifies for client to write simple code, to add, subtract, multiply, divide, input, output, increment, decrement fractions and perform Boolean logical operations on them. The Fraction class would include member and friend functions. I am giving you a suggested design of Fraction class below. Any changes in this design would need justification presented to me as an improvement.

Design the program for the user. This requires making program as user friendly as possible. Also understand that client can give you their requirement only the way they see it. Client is not obligated to give you the perfect description of their requirement that would necessarily make your job the easiest. You are however free to ask questions of clients regarding their requirements. This is the only expectation you must have from the client. As explained in two documents on software design given to you (on Student class and Student List class), you must fulfill all requirements related to algorithms, test code, output from test code, and invariant discussion and submit a document on it. I am expecting you to develop something that comes close to that can be shipped to a client. The diagram below shows the UML diagram, which only shows member data and functions. Friend functions are noted after the diagram. Diagram also notes the optional functions.

For optional functions you have an option of doing them or not. All that are not tagged as optional ARE REQUIRED. Fraction class has two invariants that must be obeyed by ALL mutators and constructor(s).

1. The denominator cannot be zero.

2. The fraction is in sign normalized and reduced form. This requirement means that fractions such as -8 /-16 and 6 /-12 must be stored as ½ , and -1/2 respectively.

Note: UML diagrams do not show friend functions and const end modifier. Use them only as high-level view of class. Diagrams are NEVER a source code.

Following Required Friend functions do not show up in UML diagram are required:

const Fraction operator+ (const Fraction & num1, const Fraction & num2)

const Fraction operator- (const Fraction & num1, const Fraction & num2)

const Fraction operator * (const Fraction & num1, const Fraction & num2)

const Fraction operator/ (const Fraction & num1, const Fraction & num2)

bool operator== (const Fraction & num1, const Fraction & num2)

bool operator!= (const Fraction & num1, const Fraction & num2)

ostream & operator<< (ostream & output, const Fraction & num)

istream & operator>> (istream & input, Fraction & num)

Description of Required Functions is below. Feel free to ask me the details of optional functions.

Private Data

Members

Optional

Optional

Return Type Public Member Functions

Fraction (long Top=0, long Bottom=1)

Default and explicit constructor. Coding must use the member initialization list! If a zero value is passed for bottom, then that case must be handled so that divide by zero problems do not occur. The constructor should also call normalize function that calls gcd function, so that by the time constructor is finished, the numerator and denominators are normalized in sign and magnitude both. Meaning of sign and magnitude normalization is as follows:

Constructor call Fraction(-8, -16) should store 1 for numerator and 2 for denominator.

Constructor call Fraction(9, -12) should store -3 for numerator and 4 for denominator.

Constructor call Fraction(-8, 16) should store -1 for numerator and 2 for denominator.

Above fulfill the invariant requirements.

Fraction operator-- ( ) Pre-decrements the fraction by one.

Fraction operator++ ( )Pre-increments the fraction by one.

Fraction operator-- (int maker)

Post-decrements the fraction by one after the current action on the object is finished.

Fraction operator++ (int maker)

Post-increments the fraction by one after the current action on the object is finished..

long getTop ( ) const

Returns the numerator

long getBottom ( ) const

Returns the denominator

void normalize ( )

Helper function that is called to normalize a Fraction. For example if during constructor call or during any mathematical operation a Fraction becomes of form such as -10/-20, then that must be normalized to 1/2 and numerator must be stored as 1 and denominator as 2. This function should call gcd function.

double toDouble ( ) const

Returns the double form of a Fraction. For example ½ would be returned as 0.5

long gcd (long a, long b)

Helper function that would assist in getting the greatest common denominator of a Fraction. This could be used by the normalize helper function. GCD function should not take sign of a, and b into account.

const string toString ( ) const – returns a string version of this Fraction.

Friend Functions

const Fraction operator+ (const Fraction & num1, const Fraction & num2)

Overloaded operator + adds the num1 Fraction to the num2 Fraction and returns the sum.

const Fraction operator- (const Fraction & num1, const Fraction & num2) Overloaded operator - subtracts from the num1 Fraction the num2 Fraction and returns the difference.

const Fraction operator * (const Fraction & num1, const Fraction & num2) Overloaded operator * multiplies the num1 Fraction with the num2 Fraction and returns the product.

const Fraction operator/ (const Fraction & num1, const Fraction & num2)

Overloaded operator / divides the num1 Fraction with the num2 Fraction and return the result. You must check to see that num2 is a non-zero Fraction in order to avoid division by zero.

bool operator== (const Fraction & num1, const Fraction & num2) Overloaded operator == tests if the num1 and num2 Fractions are same or not. Returns true if they are same otherwise returns false.

bool operator!= (const Fraction & num1, const Fraction & num2)

Overloaded operator != tests if the num1 and num2 Fractions are different or not. Returns true if they are different otherwise returns true.

ostream & operator<< (ostream & output, const Fraction & num) Overloaded insertion operator prints the Fraction either to the standard output or to a file.

istream & operator>> (istream & input, Fraction & num)

Overloaded extraction operator reads the data for a Fraction object from a file or from the standard input.

If you have problems in ostream and istream operators doing double duty (like interaction with keyboard and file both, then you can add getInstance for data input from keyboard, and add a print function to print to console). In that case istream operator must read from file and

ostream must print to file.

Private Data Members in Fraction Class

For private data members you would need two long data members – one that stores numerator, other that stores denominator. (See UML diagram).

Coding sequence of Fraction Class Fraction class needs to be perfected before FractionList class can be coded. After coding the default/explicit constructor using initialization list, you must code normalize and GCD functions.

Then you should code toString member function. At this point you must test that constructor, normalize, GCD, and toString all work correctly together to enforce the invariants. Table below shows the tests you should run and outputs you should get in each case.

Test run in main Output to console Comments Fraction F1(9, 0); Program crashes with the message to the affect that denominator cannot be zero

Use a conditional throw statement such as If(denominator == 0) Throw “Denominator cannot be zero”;

Fraction F2(-6, -12);

cout<<F2.toString()<<endl;

Prints ½ to console.

Fraction F3(9, -12);

cout<<F3.toString()<<endl;

Prints -3/4 to console

Fraction F4(8, 16);

cout<<F4.toString()<<endl;

Prints ½ to console.

Fraction F5(5, 16);

cout<<F5.toString()<<endl;

Prints 5/16 to console.

Fraction F6(-5);

cout<<F6.toString()<<endl;

Prints -5/1 to console.

Fraction F7(5, -1);

cout<<F7.toString()<<endl;

Prints -5/1 to console.

Please Do not code any other functions until all the tests above work! Time to seek my help is when one or more tests above do not work. You may have to tweak some or all four (constructor, normalize, GCD, or toString) to make it work.

Coding of friend functions that perform Fraction Arithmetic

Now proceed to code all friend functions that perform Fraction arithmetic (+, -, *, and /).

Perform tests on all four operators to ascertain that all four friend functions work correctly.

Some suggested code and output from it is given below.

Code Output

Fraction F2(6, -12);

Fraction F3(-9, 12);

cout << "F2 = " << F2.toString() << endl;

cout << "F3 = " << F3.toString() << endl;

cout << "F2 + F3 = " << (F2 + F3).toString() << endl;

cout << "F2 - F3 = " << (F2 - F3).toString() << endl;

cout << "F2 * F3 = " << (F2 * F3).toString() << endl;

cout << "F2 / F3 = " << (F2 / F3).toString() << endl;

cout << "F2 + 2 = " << (F2 + 2).toString() << endl;

cout << " 2 + F2 = " << (2+ F2).toString() << endl;

cout << "F2 - 2 = " << (F2 - 2).toString() << endl;

cout << " 2 - F2 = " << (2 - F2).toString() << endl;

cout << "F2 * 2 = " << (F2 * 2).toString() << endl;

cout << " 2 * F2 = " << (2 * F2).toString() << endl;

cout << "F2 / 2 = " << (F2 / 2).toString() << endl;

cout << " 2 / F2 = " << (2 / F2).toString() << endl;

F2 = -1/2

F3 = -3/4

F2 + F3 = -5/4

F2 - F3 = 1/4

F2 * F3 = 3/8

F2 / F3 = 2/3

F2 + 2 = 3/2

2 + F2 = 3/2

F2 - 2 = -5/2

2 - F2 = 5/2

F2 * 2 = -1

2 * F2 = -1

F2 / 2 = -1/4

2 / F2 = -4

Since constructor is by now proven to enforce invariant, it is important that all of friend functions create the Fraction object to be returned by return mechanism or by reference through the use of constructor. My Complex number or any other bridge example given to you shows this model of development. If the output from above code is not consistent with output shown in right column, then there are bugs to be removed. Seek TA/Instructor help.

Coding of Friend functions operator == and !=

Do not use toDouble or double value of fractions to compare them for equality or non-equality.

Computers do not guarantee to store floating point values accurately. At this point all binary friend operators must work correctly when used individually or combined together. The table below shows the code that you may use and the output from that code.

Test Code Output

Fraction F1(1, 3);

Fraction F2(2, 6);

Fraction F3(5, 5);

cout << boolalpha;

cout << "F1 = " << F1.toString() << endl;

cout << "F2 = " << F2.toString() << endl;

cout << "F3 = " << F3.toString() << endl;

cout << "F1 == F2 is : " << (F1 == F2) << endl;

cout << "F1 != F2 is : " << (F1 != F2) << endl;

cout << "F1 != F3 is : " << (F1 != F3) << endl;

cout << "F1 == F3 is : " << (F1 == F3) << endl;

cout << "F3 == 1 is : " << (F3 == 1) << endl;

cout << "1 == F3 is : " << (1 == F3) << endl;

cout << "F3 == 2 is : " << (F3 == 2) << endl;

cout << "2 == F3 is : " << (2 == F3) << endl;

cout << "F1 == F1 is : " << (F1 == F1) << endl;

cout << "(F1+1) == (1+F1) is : " << ((F1 +1) == (1+F1) )<< endl;

cout << "(F1*F2) == (F1*F2) is : " << ((F1*F2) == (F1*F2)) << endl;

cout << "(F1/F2) == (F1/F2) is : " << ((F1/F2) == (F1/F2)) << endl;

cout << "(F1-F2) == (F1-F2) is : " << ((F1 - F2) == (F1 - F2)) << endl;

cout << "(F1+1) == (F1+2) is : " << ((F1 + 1) == (F1 + 2)) << endl;

cout << "(F1*F2) == (F1*F3) is : " << ((F1*F2) == (F1*F3)) << endl;

cout << "(F1/F2) == (F1/F3) is : " << ((F1 / F2) == (F1 / F3)) << endl;

cout << "(F1-F2) == (F1-F3) is : " << ((F1 - F2) == (F1 - F3)) << endl;

F1 = 1/3

F2 = 1/3

F3 = 1

F1 == F2 is : true

F1 != F2 is : false

F1 != F3 is : true

F1 == F3 is : false

F3 == 1 is : true

1 == F3 is : true

F3 == 2 is : false

2 == F3 is : false

F1 == F1 is : true

(F1+1) == (1+F1) is : true

(F1*F2) == (F1*F2) is : true

(F1/F2) == (F1/F2) is : true

(F1-F2) == (F1-F2) is : true

(F1+1) == (F1+2) is : false

(F1*F2) == (F1*F3) is : false

(F1/F2) == (F1/F3) is : false

(F1-F2) == (F1-F3) is : false

If this test does not work, that means that debugging of your code is needed. Seek instructor/TA help.

Coding of friend functions istream and ostream operators. [If you chose to add getInstance and print functions then they must also be added at this point).

Now you code friend functions istream and ostream operators. I am going to assume that istream operator works both with keyboard data entry or read from file. If that is not the case then modify the code below accordingly. Notice that both cascaded input and output also must work correctly. The table below gives source code and output that it must create. The input file contained a fraction as 6 -12. First value is numerator and second denominator.

Source code Output

Fraction F1;

Fraction F2;

Fraction F3;

cin >> F1>>F3;//Tests cascaded input

cout << F1 << endl << F3 << endl;//Tests cascaded output

cout << "Enter full path to file from which to read the fraction: ";

cin.sync();//Use ignore on Mac

string inputFile;

getline(cin, inputFile);

ifstream in(inputFile);

Enter numerator: -8

Enter denominator: -16

Enter numerator: 9

Enter denominator: -12

1/2

-3/4

Enter full path to file from which to read

the fraction: SingleFraction1.txt

Enter full path to file to which to output

the Fraction: OutSingleFraction1.txt

\

if (!in.is_open()){

cout << "Failed to open input file. Exiting program." << endl;

exit(0);

}

in >> F2;//Tests input from file

cout << "Enter full path to file to which to output the Fraction: ";

cin.sync();//Use ignore on Mac

string outFile;

getline(cin, outFile);

ofstream out(outFile);

if (!out.is_open()){

cout << "Failed to create output file. Exiting program." <<

endl;

exit(0);

}

cout << "The fraction read from file is: " << F2 << endl;

cout << "Now wrting to file." << endl;

out << F2;//Tests output to file

in.close();

out.close();

The fraction read from file is: -1/2

Now writing to file.

If any part of this test does not provide expected results then seek TA/Instructor help.

Coding of member functions pre and post fix increment and decrement operators

Please read the relevant portion from e-book and also review code for Complex Number class where overloading of pre and post increment and decrement operators are described. Table below shows the test code and its output. Do not test the post and prefix operators using cascaded output. C++ does not define the behavior when insertion operator << is mixed with ++ and - - in cascaded output.

Source Code [Cascaded output is not to be used because its combination with multiple use of ++ and – operators is undefined].

Output

Fraction F1(-8, -16);

Fraction F2(6, -12);

cout << "F1 = " << F1 << endl;

cout << ++F1 << endl;

cout << F1++ << endl;

cout<< F1<< endl;

cout << "F2 = " << F2 << endl;

cout << --F2 << endl;

cout << F2-- << endl;

cout << F2 << endl;

F1 = 1/2

3/2

3/2

5/2

F2 = -1/2

-3/2

-3/2

-5/2

Coding of Non mutator member functions

Now you can code non-mutator functions such as getTop, getBottom, and toDouble, or any other remaining functions. Table below shows the test code and output for these.

Source Code Output

Fraction F(-8, -16);

cout << "Fraction is: " << F << endl;

cout << "The numerator is: " << F.getTop() << endl;

cout << "The denominator is: " << F.getBottom() << endl;

cout << "The double value is: " << F.toDouble() << endl;

Fraction is: 1/2

The numerator is: 1

The denominator is: 2

The double value is: 0.5

FractionList Class

The other class that you would need to design would be a FractionList Class, which would store an array of Fraction objects. Suggested design of FractionList class is given by the UML below.

The required friend functions are:

friend ostream & operator <<(ostream & out, const FractionList & FL);

friend istream & operator >>(istream & in, FractionList & FL);

The objectives the class FractionList should fulfill are:

1. I should be able to read a file that contains numerator and denominator of Fractions to be stored in an array (private member of FractionList class) from a text file. The structure of text file is shown in the file that I would be using for testing your program. (This file is stored on the

network. In all programs I would also test your program with an empty file.) Maximum number of Fractions to be stored would be 30.

2. I should be able to print to an output file or to standard output the followings:

All the Fractions and their corresponding double values

Sum of all the Fractions as a Fraction and the corresponding double value.

Optional

Optional

Mean of all the Fractions as a Fraction and the corresponding double value.

Obviously to fulfill these objectives your FractionList class would have member functions and data members. See UML diagram for required functions. You will be graded on the soundness of your design as you have been in past.

Coding Sequence

As we have done, with list type programs in past, you need to complete coding of following member functions/constructor, before you begin to test your code with code snippets.

1. Constructor

2. addFraction

3. toString

Once you have successfully coded above, the table below shows the mapping between test code and output will be. Your program should show that source code in left column shows the output in right column.

Source Code Output

Fraction F1(-8, -16);

Fraction F2(6, -12);

Fraction F3(9, -12);

Fraction F4(5, 6);

Fraction F5(-11, 17);

FractionList FL;

FL.addFraction(F1);

FL.addFraction(F2);

FL.addFraction(F3);

FL.addFraction(F4);

FL.addFraction(F5);

cout << "The Fraction List is: " <<endl<< FL.toString() << endl;

The Fraction List is:

1/2

-1/2

-3/4

5/6

-11/17

Please seek TA/instructor help if you do not get above output.

Write the sort, isEmpty, isFull, getNumberOfElements functions

You can write sort function at minimum using bubble sort. The sort function must sort the FractionList in ascending order, and set isSorted data member to true. If code of above table is run after the call to sort function then additional code and output are shown in table below. In addition we show call to (by now coded) functions: isEmpty(), isFull(), and getNumberOfElements functions.

Source code Output

//Add Source code of previous table plus below

FL.sort();

cout << "After ascending order sort, the FractionList is:" <<

endl<< FL.toString() << endl;

cout << boolalpha;

cout << "It is " << FL.isEmpty() << " that list is empty()" << endl;

cout << "It is " << FL.isFull() << " that list is full()" << endl;

cout << "The list has " << FL.getNumberOfElements()

<< " fractions." << endl;

Output in previous table and then

below:

After ascending order sort, the

FractionList is:

-3/4

-11/17

-1/2

1/2

5/6

It is false that list is empty()

It is false that list is full()

The list has 5 fractions.

Code friend functions, istream and ostream operators to read from file and write Fractions to output file

Now you need to code istream operator to read many fractions from a file. The stored fractions are in the following format: Each fraction is stored as space delimited token in format numerator denominator. One such file is given below:

2 3 4 5 99 9 12 4 4 67 89 89

In the above file, the first fraction is 2/3, second 4/5 and so on. Program test code must be able to read this file and display to console and output file. The code sample to test this part of your program and its output are given below.

Source code Output

FractionList FL;

cout << "Enter full path to file from which to read the fractions: ";

cin.sync();//Use ignore on Mac

string inputFile;

getline(cin, inputFile);

ifstream in(inputFile);

if (!in.is_open()){

cout << "Failed to open input file. Exiting program." << endl;

exit(0);

}

in >> FL;

cout << "Enter full path to file to which to output the Fractions: ";

cin.sync();//Use ignore on Mac

string outFile;

getline(cin, outFile);

ofstream out(outFile);

if (!out.is_open()){

cout << "Failed to create output file. Exiting program." << endl;

exit(0);

}

cout << "The fraction list outputted to console: " << endl;

cout << FL << endl;

cout << "Now writing fraction list to an output file." << endl;

out << FL << endl;

in.close();

out.close();

Enter full path to file from which to read the

fractions: ManyFractions.txt

Enter full path to file to which to output the

Fractions:

OutManyFractions.txt

The fraction list outputted to console:

2/3

4/5

11

3

4/67

1

Now writing fraction list to an output file.

The output in file would be same as the one shown in right column for console. Seek instructor/TA help if your output does not map with the output shown above. You can also now write getInstance that should work to add many fractions from the keyboard input.

Coding getLargest, getSumOfFractions, getSortState, and operator []

Function getLargest should find the largest fraction in entire Fraction list and return its value as a const Fraction. getSumOfFractions sums up all Fractions and returns the sum as a Fraction.

getSortState function returns true if FractionList is sorted, otherwise it returns false. Operator [] is overloaded to return the Fraction at an inbound index in the List array inside the Fraction. Test your newly written functions with the code snippet below and confirm that your output maps with the right column output in the table below:

Source Code Output

Fraction F1(-8, -16);

Fraction F2(6, -12);

Fraction F3(9, -12);

Fraction F4(5, 6);

Fraction F5(-11, 17);

FractionList FL;

FL.addFraction(F1);

FL.addFraction(F2);

FL.addFraction(F3);

FL.addFraction(F4);

FL.addFraction(F5);

cout << "The Fraction List is: " << endl << FL.toString() << endl;

cout << "The largest fraction is:" << FL.getLargest() << endl;

cout << "The very first fraction in the array is :" << FL[0] << endl;

cout << "The very last fraction in the array is :"

<< FL[FL.getNumberOfElements() -1] << endl;

cout << "The sum of all Fractions is: "

<< FL.getSumOfFractions() << endl;

cout << boolalpha;

cout << "It is " << FL.getSortState() << " that Fraction List is sorted."

<< endl;

FL.sort();

cout << "It is " << FL.getSortState() << " that Fraction List is sorted."

<< endl;

FL.sort();

The Fraction List is:

1/2

-1/2

-3/4

5/6

-11/17

The largest fraction is:5/6

The very first fraction in the array is :1/2

The very last fraction in the array is :-11/17

The sum of all Fractions is: -115/204

It is false that Fraction List is sorted.

It is true that Fraction List is sorted.

If you have bugs in any of the functions written in this section seek TA/Instructor help.

Finish writing and testing any other leftover functions now.

Design of driver Program:

The driver program should have a menu driven main function that should have following menus with qualitative description of each item. I have described approximate names of menu items.

Feel free to invent better fitting names.

Main Menu:

1. Create Fraction List from input file data entry. [User should be able to specify the file name at runtime. File name error checking should be done].

2. Create Fraction List from keyboard data entry.

3. Print the Fraction List to console using operator >> and toString both.

4. Print the Fraction List to output file using operator >> and toString both.

5. Sort and FractionList to console only using operator >> only.

6. Print sum of all Fractions in the list, the largest fraction in the list, sort state (sorted or not) to console only.

7. Demonstrate Arithmetical Operations. [Asks user to enter two fractions from keyboard, and demonstrate proper functioning of *, /, -, and + operators. Also shows that following operations also work:

2 + Fraction

Fraction + 2

2 - Fraction

Fraction – 2

2 * Fraction

Fraction * 2

2 / Fraction

Fraction / 2

]

8. Demonstrate pre and post fix increment and decrement operators. [This menu asks user to enter two fractions from keyboard. Output shows the successful completions of ++F, F++, --F1, and F1—operations where F and F1 are user entered Fractions.

9. Demonstrates the operation of overloaded operator [ ]. [Menu asks user the index at which Fraction of interest may be in the array. If index is in-bound, it is outputted to console.

Otherwise a message from exception handler is displayed].

10. Exit.

**Subject Computer Science C-Family Programming**