Using C++ operator overloading to make a simple fraction class






4.47/5 (11 votes)
Shows a sample of operator overloading in C++
Introduction
This article shows an example of using C++ operator overloading to make a simple class that will accept fractions from the standard input and output them to the standard output.
It will also reduce (normalize) the entered fraction. The code is commented pretty heavily, so it should be pretty easy to follow.
This class is intended to be a sample of operator overloading, so it doesn't have a whole lot of functionality. My hope is that its simplicity will make it easy to understand.
What is operator overloading?
C++ only understands how to use operators (=
, <<
, >>
) on types it knows about such as int
, char
, long
, etc.
If you want to take advantage of these operators with your custom types then you will have to "overload" the operators you want to use with your custom type.
In this example I have overloaded the << and >> operators for writing to the standard input and output to accept fractions in the format n/d, n\d, or just plain n (n = numerator, d = denominator).
Overloading << and >>
If you take a look inside the Fraction.h header file you will see that we have told the compiler we want to overload both the <<
and >>
operators for CFraction
objects.
// Overloading of the iostream << and >> operators // These must be declared ouside our class, with global scope ostream& operator << (ostream& os, CFraction& fraction); istream& operator >> (istream& is, CFraction& fraction);
Writing a fraction to stdout
Here is the function that handles the <<
operator. It takes care of formatting a CFraction
object and writing it to the standard output.
// Overloaded iostream << operator // // When the << operator is called with a CFraction on // the right our overloaded function is called. We // are passed a reference to the stream and the CFraction // to the right. We are free to output to the // stream anthing we want as a result. // // We return the ostream& so that we can put multiple // << operations on a single line ostream& operator << (ostream& os, CFraction& fraction) { // Output the fraction to the stream in the format n/d os << fraction.GetNumerator(); os << '/'; os << fraction.GetDenominator(); return os; }
Reading a fraction from stdin
// Overloaded iostream >> operator // // I tried to write this so it is easy to understand. // // This will be called whenever the >> operator is used // with a CFraction object to the right of it. // What happens is the system passes a reference to the // stream to the left of the >>, and also // a reference to the CFraction object. At this point // you are responsbile for accepting and parsing input for // the stream passed to you. // // We return the istream& so that we can put multiple >> // operations on a single line istream& operator >> (istream& is, CFraction& fraction) { // Buffer to hold the input char input[64]; // Get a line from the input stream passed is.getline(input, 64); // This variable will hold a pointer to the '/' or '\' seperator char *slash_pos = NULL; // Try to file a '/' slash_pos = strchr(input, '/'); // If we could not find a '/' look for a '\' if (slash_pos == NULL) slash_pos = strchr(input, '\\'); // If we didn't find either if (slash_pos == NULL) { // Was anything entered? if (strlen(input)) { // If so try to convert it to a number int numerator = atoi(input); // If we got a number then we'll make it numer_entered/1 if (numerator) { fraction.SetNumerator(numerator); fraction.SetDenominator(1); return is; // Else we will set both the numerator and denominator to 0 } else { fraction.SetNumerator(0); fraction.SetDenominator(0); return is; } // There was nothing entered, set both the // numerator and denominator to 0 } else { fraction.SetNumerator(0); fraction.SetDenominator(0); return is; } } // Variable to hold the numerator string char str_numerator[32]; // Copy the numerator out of the input string memcpy((void *)str_numerator, (void *)input, (slash_pos - input)); // Variable to hold the integer version of the numerator int numerator = 0; // Convert the string to an integer numerator = atoi(str_numerator); // If we got a non-zero value then set the fraction's numerator if (numerator) { fraction.SetNumerator(numerator); // If we got zero then the fraction is 0, so set both // the numerator and denominator to zero } else { fraction.SetNumerator(0); fraction.SetDenominator(0); return is; } // Variable to hold the denominator char str_denominator[32]; // Copy the denominator out of the input string memcpy((void *)str_denominator, (void *)(slash_pos+1), ((input + strlen(input)) - slash_pos)); // Variable to hold the integer version of the denominator int denominator = 0; // Convert the string to an integer denominator = atoi(str_denominator); // Did we get a non-zero number for the denominator? // If so, set it if (denominator) { fraction.SetDenominator(denominator); // If not then the fraction is undefined, set both the // numerator and denominator to zero } else { fraction.SetNumerator(0); fraction.SetDenominator(0); } return is; }
Sample Application
This is the source code for the sample console application that uses the class. It is very straightforward.
int main(void) { // Test the three constructors and << operator // Supply both numerator and demoninator CFraction *obj = new CFraction(2, 3); cout << "Testing (2/3): \t\t"; cout << *obj << endl; delete obj; // Supply numerator only obj = new CFraction(4); cout << "Testing (4): \t\t"; cout << *obj << endl; delete obj; // Default constructor with no arguments obj = new CFraction(); cout << "Testing (no args): \t"; cout << *obj << endl << endl; delete obj; // Accept fractions and display them before and after normalization // until ctrl-c is pressed while(1) { obj = new CFraction(); cout << "Enter a fraction (ctrl-c to exit): "; // This calls our overloaded >> operator because // a CFraction object is to the right of it cin >> *obj; // The << *obj calls our overloaded << operator because // a CFraction object is to the right of it cout << endl << "You entered (before normalization): " << *obj; obj->Normalize(); // The << *obj calls our overloaded << operator because // a CFraction object is to the right of it cout << endl << "You entered (after normalization): " << *obj << endl << endl; delete obj; } }