< Free Open Study > 
Fraction Class
Problem Write and test a C++ class that represents a fraction.
Discussion A fraction is made up of a numerator and a denominator, so our fraction class must have data members for each of these. What operations do we normally apply to fractions? First we must initialize a fraction by storing values into the numerator and the denominator, and we need member functions that return the numerator and the denominator. Another operation would reduce the fraction to its lowest terms. We should also be able to test whether the fraction is equal to zero or greater than 1. If the fraction is greater than or equal to 1 (not a proper fraction), we should have an operation that converts the fraction to a whole number and a fraction. There are binary operations on fractions, but we are asked only to write and test a class that represents a fraction. Binary operations could be added later.
Let's summarize what we have said so far using a CRC card. A CRC card is a 4" x 6" or a 5" × 8" card on which we record the name of the class, the responsibilities, and the classes with which the class collaborates. CRC cards are used frequently in objectoriented design, and we discuss them in more detail in later chapters. Here we use one to record what we have decided our fraction class must do. We call the actions that the class must perform the responsibilities of the class. We use a handwriting font to indicate that CRC cards are a pencil and paper tool. We change to a monospaced font for the operations when we are talking about their implementation.
Before we translate this CRC card into a class definition in C++, let's examine each operation again. Let's change the expressions for the responsibilities into function names. The Initialize operation takes two integer values and stores them into the data members of the class. Let's call these data members num and denom. NumeratorIs and DenominatorIs return the values of the data members.
Reduce checks whether the numerator and the denominator have a common factor and, if they do, divides both by the common factor. On second thought, should making sure that the fraction is in reduced form be left to the user of the fraction class? If a fraction is not reduced to its lowest terms, binary arithmetic operations could cause overflow problems; the sizes of the numerator and denominator could become quite large. Let's remove this operation as a member function and make it a precondition for instances of our fraction class. If binary operations are added to the class, it becomes the responsibility of these operations to reduce the resulting fraction to its reduced form.
Iszero tests whether the fraction is zero. How do we represent zero as a fraction? The numerator is zero and the denominator is 1, so IsZero tests whether the numerator is zero. IsGreaterThanOrEqualToOne is too long an identifier. Let's call the operation that tests to see if the numerator is greater than or equal to the denominator IsNotProper. ConvertToProper returns the wholenumber part and leaves the remaining part in the fraction.
We are now ready to write the class definition. We know what each operation should do. What about the preconditions for the operations? All fractions involved must be initialized before the member functions are called and must be in reduced form. ConvertToProper should be called only if the fraction is improper.
class FractionType { public: void Initialize(int numerator, int denominator): // Function: Initialize the fraction // Pre: Numerator and denominator are in reduced form // Post: Fraction is initialized int NumeratorIs(); // Function: Returns the value of the numerator // Pre: Fraction has been initialized // Post: Numerator is returned int DenominatorIs(); // Function: Returns the value of the denominator // Pre: Fraction has been initialized // Post: Denominator is returned bool IsZero(); // Function: Determines if fraction is zero // Pre: Fraction has been initialized // Post: Returns true if numerator is zero, false otherwise bool IsNotProper(); // Function: Determines if fraction is a proper fraction // Pre: Fraction has been initialized // Post: Returns true if fraction is greater than or equal to 1; false // otherwise int ConvertToProper(); // Function: Converts the fraction to a whole number and a // fractional part // Pre: Fraction has been initialized, is in reduced form, and // is not a proper fraction // Post: Returns whole number // Remaining fraction is original fraction minus the // whole number: fraction is in reduced form private: int num; int denom; };
At this stage, before we write any code for the member functions, we can write our test driver using the algorithm shown in the last section. Let's call the instance of the FractionType fraction. Here is the portion of the algorithm that we must write:
while... Execute the command by invoking the member function of the same name Print the results to the output file ...
We have six member functions to test. We can set up an ifthenelse statement comparing the input operation to the member function names. When the name matches, the function is called and the result is written to the output file.
if (command is "Initialize") Read numerator Read denominator fraction.Initialize(numerator, denominator) Write on outFile "Numerator:", fraction.NumeratorIs() "Denominator:", fraction.DenominatorIs() else if (command is "NumeratorIs") Write on outFile "Numerator:", fraction.NumeratorIs() else if (command is "DenominatorIs") Write on outFile "Denominator:", fraction.DenominatorIs() else if (command is "IsZero") if (fraction.IsZero) Write on outFile "Fraction is zero" else Write on outFile "Fraction is not zero" else if (command is "IsNotProper") if (fraction.IsNotProper()) Write on outFile "Fraction is improper" else Write on outFile "Fraction is proper" else Write on outFile " Whole number is", (fraction.ConvertToProper()) Write on outFile "Numerator:", fraction.NumeratorIs() "Denominator:", fraction.DenominatorIs()
The file containing the specification of class FractionType is in file "frac.h". Here are the pieces that must be added to the generalized test driver to test this class:
#include "frac.h" // File containing the class to be tested FractionType fraction; // Declaration of FractionType object while (command != "Quit") { if (command == "Initialize") { int numerator, denominator; inFile >> numerator; inFile >> denominator; fraction.Initialize(numerator, denominator); outFile << "Numerator: " << fraction.NumeratorIs() << " Denominator: " << fraction.DenominatorIs() << end1; ) else if (command == "NumeratorIs") outFile << "Numerator: " << fraction.NumeratorIs() << end1; else if (command == "DenominatorIs") outFile << "Denominator: " << fraction.DenominatorIs() << end1; else if (command == "IsZero") if (fraction.IsZero()) outFile << "Fraction is zero " << end1; else outFile << "Fraction is not zero " << end1; else if (command == "IsNotProper") if (fraction.IsNotProper()) outFile << "Fraction is improper " << end1; else outFile << "Fraction is proper " << end1; else { outFile << "Whole number is " << fraction.ConvertToProper() << end1; outFile << "Numerator: " << fraction.NumeratorIs() << " Denominator: " << fraction.DenominatorIs() << end1: } : }
We have the test driver and the specification file containing the class. Now we must write the code for the function definitions and write and implement the test plan. The algorithms for the first five functions are so straightforward that they can be written with no further comment. The fifth function, ConvertToProper, must return the wholenumber integer. It is extracted by taking the integer result of dividing the denominator into the numerator. The integer remainder becomes the numerator of the remaining fraction, and the denominator remains the same. If the numerator of the remaining fraction is zero, we must set the denominator to 1 to be consistent with the definition of a zero fraction.
// Implementation file for class FractionType #include "frac.h" void FractionType::Initialize(int numerator, int denominator) // Function: Initialize the fraction // Pre: numerator and denominator are in reduced form // Post: numerator is stored in num; denominator is stored in // denom { num = numerator; denom = denominator; } int FractionType::NumeratorIs() // Function: Returns the value of the numerator // Pre: Fraction has been initialized // Post: numerator is returned { return num; } int FractionType::DenominatorIs() // Function: Returns the value of the denominator // Pre: Fraction has been initialized // Post: denominator is returned { return denom: } bool FractionType::IsZero() // Function: Determines if fraction is zero // Pre: Fraction has been initialized // Post: Returns true if numerator is zero; false otherwise { return (num == 0); } bool FractionType::IsNotProper() // Function: Determines if fraction is a proper fraction // Pre: Fraction has been initialized // Post: Returns true if num is greater than or equal to denom; false // otherwise { return (num >= denom); } int FractionType::ConvertToProper() // Function: Converts the fraction to a whole number and a // fractional part // Pre: Fraction has been initialized, is in reduced form, and // is not a proper fraction // Post: Returns num divided by denom // num is original num % denom; denom is not changed { int result; result = num / denom; num == num % denom; if (num == 0) denom = 1; return result; }
We have six member functions to test. Two of the six are Boolean functions, so we need two test cases for each. Here, then, is a test plan that has eight cases. Note that we have to initialize the fraction three times: once for a proper fraction, once for an improper fraction, and once for zero.
Here are the input file, the output file, and a screen shot from the run:
Input File 
Output File 

Initialize 
Test_Run_for_FractionType 
3 
Numerator: 3 Denominator: 4 
4 
Fraction is not zero 
IsZero 
Fraction is proper 
IsNotProper 
Numerator: 3 
NumeratorIs 
Denominator: 4 
DenominatorIs 
Numerator: 4 Denominator: 3 
Initialize 
Fraction is improper 
4 
Whole number is 1 
3 
Numerator: 1 Denominator: 3 
IsNotProper 
Numerator: 0 Denominator: 1 
ConvertToProper 
Fraction is zero 
Initialize 

0 

1 

IsZero 

Quit 
< Free Open Study > 
Converted from CHM to HTML with chm2web Pro 2.85 (unicode) 