Previous sections presented abridged versions of the fraction class and many of its functions as examples of overloaded operators, implemented as either member or friend functions. This section presents the complete and elaborated example, organizing it into multiple figures to simplify the presentation.
fraction operator+(fraction f1, fraction f2)
{
int n = f1.numerator * f2.denominator +
f2.numerator * f1.denominator;
int d = f1.denominator * f2.denominator;
return fraction(n, d);
}
fraction operator-(fraction f1, fraction f2)
{
int n = f1.numerator * f2.denominator -
f2.numerator * f1.denominator;
int d = f1.denominator * f2.denominator;
return fraction(n, d);
}
fraction operator*(fraction f1, fraction f2)
{
int n = f1.numerator * f2.numerator;
int d = f1.denominator * f2.denominator;
return fraction(n, d);
}
fraction operator/(fraction f1, fraction f2)
{
int n = f1.numerator * f2.denominator;
int d = f1.denominator * f2.numerator;
return fraction(n, d);
}
fraction.cpp: fraction arithmetic operators.
The class implements the arithmetic operators as friend functions - not members. So, the program must explicitly pass all arguments in parentheses and use the parameter names when accessing the objects' member variables. The advantage of constructing the new fraction at the end of the function, after it calculates the numerator and denominator, is that it allows the constructor to reduce the fraction to the lowest terms.
fraction.cpp: fraction I/O operators.
The overloaded I/O operators follow a rigid pattern, but each version is overloaded for a different class, implying that their bodies contain unique instructions. The functions' instructions or statements
depend on the target class' member variables. These functions target the fraction class, accessing its numerator and denominator.
Programmers choose how a printed fraction object should look, and format it accordingly. The out parameter may refer to any output stream - the console, a file, etc.
The input operator may receive data from the console, a file, a network, etc. Removing the prompts generalizes the function, allowing it to read data from any of these sources efficiently. The stream object in may be bound to any input stream drawing data from these sources.
int gcd(int u, int v)
{
u = (u < 0) ? -u : u;
v = (v < 0) ? -v : v;
while (u > 0)
{
if (u < v)
{
int t = u; // swap u and v
u = v;
v = t;
}
u -= v;
}
return v; // the GCD of u and v
}
void fraction::reduce()
{
int common = gcd(numerator, denominator);
numerator /= common;
denominator /= common;
}
(a)
(b)
fraction.cpp: helper functions.
All functions provide code reusability by grouping and naming operations that programs use by calling them. Furthermore, they allow programmers to decompose large problems into smaller ones. Programmers implement small solutions as functions and incorporate them into larger ones through function calls. Helper functions work similarly but limit their actions to objects instantiated from a single class.
The gcd function implements Euclid's algorithm for finding the greatest common divisor. The fraction class uses the function to reduce a fraction to its lowest terms. However, the function is only used internally and is not a service typically associated with fractions. So, programmers exclude it from the class's public interface by making it private. See Recursion V. Iteration.
The fraction class automatically reduces a fraction object when it creates or inputs it. By putting the statements in a helper function, the class avoids duplicating them in the constructors and operator>>. The function is appropriately private because all fraction operations reduce the objects when creating or changing them, eliminating the need for applications to perform that operation. If it becomes possible for applications to create unreduced fractions, programmers can make the function public.