The Rational Class with Overloaded Function Operators in C++

This section revises the Rational class with overloaded function operators.

The preceding sections introduced how to overload function operators. The following points are worth noting:

  • Conversion functions from a class type to a primitive type or from a primitive type to a class type cannot both be defined in the same class. Doing so would cause ambigu­ity errors, because the compiler cannot decide which conversion to perform. Often converting from a primitive type to a class type is more useful. So, we will define our Rational class to support automatic conversion from a primitive type to the Rational
  • Most operators can be overloaded either as member or nonmember functions. How­ever, the =, [], ->, and () operators must be overloaded as member functions and << and >> operators must be overloaded as nonmember functions.
  • If an operator (i.e., +, -, *, /, %, <, <=, ==, !=, >, and >=) can be implemented either as a member or nonmember function, it is better to overload it as a nonmember func­tion to enable automatic type conversion with symmetric operands.
  • If you want the returned object to be used as an Lvalue (i.e., used on the left-hand side of the assignment statement), you need to define the function to return a refer­ence. The augmented assignment operators +=, -=, *=, /=, and %=, the prefix ++ and — operators, the subscript operator [], and the assignment operators = are Lvalue operators.

Listing 14.7 gives a new header file named RationalWithOperators.h for the Rational class with function operators. Lines 10-22 in the new file are the same as in Listing 14.1 Rational.h. The functions for augmented assignment operators (+=, -=, *=, /=), subscript operator [], prefix ++, and prefix — are defined to return a reference (lines 27-37). The stream extraction << and stream insertion >> operators are defined in lines 48-49. The nonmember functions for comparison operators (<, <=, >, >=, ==, !=) and arithmetic operators (+, -, *, /) are defined in lines 57-69.

Listing 14.7 RationalWithOperators.h

1 #ifndef RATIONALWITHOPERATORS_H
2
#define RATIONALWITHOPERATORS_H
3
#include <string>
4
#include <iostream>
5
using namespace std;
6
7
class Rational
8 {
public:
10    Rational();
11    Rational(
int numerator, int denominator);
12   
int getNumerator() const;
13   
int getDenominator() const;
14    Rational add(
const Rational& secondRational) const;
15    Rational subtract(
const Rational& secondRational) const;
16    Rational multiply(
const Rational& secondRational) const;
17    Rational divide(
const Rational& secondRational) const;
18   
int compareTo(const Rational& secondRational) const;
19   
bool equals(const Rational& secondRational) const;
20   
int intValue() const;
21   
double doubleValue() const;
22    string toString()
const;
23
24    Rational(
int numerator); // Suitable for type conversion
25
26   
// Define function operators for augmented operators
27    Rational& operator+=(const Rational& secondRational);
28    Rational&
operator-=(const Rational& secondRational);
29    Rational&
operator*=(const Rational& secondRational);
30    Rational&
operator/=(const Rational& secondRational);
31
32   
// Define function operator [] 33    int& operator[](int index);
34
35   
// Define function operators for prefix ++ and —
36    Rational& operator++();
37    Rational&
operator–();
38
39   
// Define function operators for postfix ++ and —
40    Rational operator++(int dummy);
41    Rational
operator–(int dummy);
42
43   
// Define function operators for unary + and –
44    Rational operator+();
45    Rational
operator-();
46
47   
// Define the << and >> operators
48    friend ostream& operator<<(ostream& , const Rational&);
49   
friend istream& operator>>(istream& , Rational&);
50
51   
private:
52   
int numerator;

53    int denominator;
54   
static int gcd(int n, int d);
55 };
56
57
// Define nonmember function operators for relational operators
58 bool operator<(const Rational& r1, const Rational& r2);
59
bool operator<=(const Rational& r1, const Rational& r2);
60
bool operator>(const Rational& r1, const Rational& r2);
61
bool operator>=(const Rational& r1, const Rational& r2);
62
bool operator==(const Rational& r1, const Rational& r2);
63
bool operator!=(const Rational& r1, const Rational& r2);
64
65
// Define nonmember function operators for arithmetic operators
66 Rational operator+(const Rational& r1, const Rational& r2);
67 Rational
operator-(const Rational& r1, const Rational& r2);
68 Rational
operator*(const Rational& r1, const Rational& r2);
69 Rational
operator/(const Rational& r1, const Rational& r2);
70
71
#endif

Listing 14.8 implements the header file. The member functions for augmented assignment oper­ators +=, -=, *=, and /= change the contents of the calling object (lines 120-142). You have to assign the result of the operation to this. The comparison operators are implemented by invok­ing r1.compareTo(r2) (lines 213-241). The arithmetic operators +, -, *, and / are imple­mented by invoking the functions add, subtract, mul tiply, and divide (lines 244-262).

Listing 14.8 RationalWithOperators.cpp

1 #include “RationalWithOperators.h”
2 #include <sstream>
3
#include <cstdlib> // For the abs function
4 Rational::Rational()
5 {
6    numerator =
0;
7    denominator =
1;
8 }
9
10 Rational::Rational(
int numerator, int denominator)
11 {
12   
int factor = gcd(numerator, denominator);
13   
this->numerator = (denominator > 0 ? 1 : -1) * numerator / factor;
14   
this->denominator = abs(denominator) / factor;
15 }
16
17
int Rational::getNumerator() const
18 {
19   
return numerator;
20 }
21
22
int Rational::getDenominator() const
23 {
24   
return denominator;
25 }
26
27
// Find GCD of two numbers
28 int Rational::gcd(int n, int d)
29 {
30     
int n1 = abs(n);
31   
int n2 = abs(d);

32    int gcd = 1;
33
34   
for (int k = 1; k <= n1 && k <= n2; k++)
35    {
36       
if (n1 % k == 0 && n2 % k == 0)
37       gcd = k;
38    }
39
40   
return gcd;
41 }
42
43 Rational Rational::add(
const Rational& secondRational) const
44 {
45   
int n = numerator * secondRational.getDenominator() +
46    denominator * secondRational.getNumerator();
47   
int d = denominator * secondRational.getDenominator();
48   
return Rational(n, d);
49 }
50
51 Rational Rational::subtract(
const Rational& secondRational) const
52 {
53   
int n = numerator * secondRational.getDenominator()
54    – denominator * secondRational.getNumerator();
55   
int d = denominator * secondRational.getDenominator();
56   
return Rational(n, d);
57 }
58
59 Rational Rational::multiply(
const Rational& secondRational) const
60 {
61   
int n = numerator * secondRational.getNumerator();
62   
int d = denominator * secondRational.getDenominator();
63   
return Rational(n, d);
64 }
65
66 Rational Rational::divide(
const Rational& secondRational) const
67 {
68   
int n = numerator * secondRational.getDenominator();
69   
int d = denominator * secondRational.numerator;
70   
return Rational(n, d);
71 }
72
73
int Rational::compareTo(const Rational& secondRational) const
74 {
75    Rational temp = subtract(secondRational);
76   
if (temp.getNumerator() < 0)
77       
return -1;
78   
else if (temp.getNumerator() == 0)
79       
return 0;
80   
else
81       return 1;
82 }
83
84
bool Rational::equals(const Rational& secondRational) const
85 {
86     
if (compareTo(secondRational) == 0)
87       
return true;
88     
else
89         return false;
90 }
91

92 int Rational::intValue() const
93 {
94     
return getNumerator() / getDenominator();
95 }
96
97
double Rational::doubleValue() const
98 {
99     
return 1.0 * getNumerator() / getDenominator();
100 }
101
102 string Rational::toString()
const
103 {
104     stringstream ss;
105    ss << numerator;
106
107   
if (denominator > 1)
108    ss <<
“/” << denominator;
109
110     
return ss.str();
111 }
112
113 Rational::Rational(
int numerator) // Suitable for type conversion
114 {
115     
this->numerator = numerator;
116     
this->denominator = 1;
117 }
118
119
// Define function operators for augmented operators
120 Rational& Rational::operator+=(const Rational& secondRational)
121 {
122    *
this = add(secondRational);
123   
return *this;
124 }
125
126 Rational& Rational::
operator-=(const Rational& secondRational)
127 {
128     *
this = subtract(secondRational);
129     
return *this;
130 }
131
132 Rational& Rational::
operator*=(const Rational& secondRational)
133 {
134    *
this = multiply(secondRational);
135   
return *this;
136 }
137
138 Rational& Rational::
operator/=(const Rational& secondRational)
139 {
140    *
this = divide(secondRational);
141   
return *this;
142 }
143
144
// Define function operator [] 145 int& Rational::operator[](int index)
146 {
147     
if (index == 0)
148       
return numerator;
149   
else
150        return denominator;
151 }

152
153
// Define function operators for prefix ++ and —
154 Rational& Rational::operator++()
155 {
156     numerator += denominator;
157     
return *this;
158 }
159
160 Rational& Rational::
operator–()
161 {
162    numerator -= denominator;
163   
return *this;
164 }
165
166
// Define function operators for postfix ++ and —
167 Rational Rational::operator++(int dummy)
168 {
169     Rational temp(numerator, denominator);
170     numerator += denominator;
171     
return temp;
172 }
173
174 Rational Rational::
operator–(int dummy)
175 {
176    Rational temp(numerator, denominator);
177    numerator -= denominator;
178   
return temp;
179 }
180
181
// Define function operators for unary + and –
182 Rational Rational::operator+()
183 {
184   
return *this;
185 }
186
187 Rational Rational::
operator-()
188 {
189   
return Rational(-numerator, denominator);
190 }
191
192
// Define the output and input operator
193 ostream& operator<<(ostream& out, const Rational& rational)
194 {
195   
if (rational.denominator == 1)
196       out << rational.numerator;
197   
else
198       out << rational.numerator << “/” << rational.denominator;
199   
return out;
200 }
201
202 istream&
operator>>(istream& in, Rational& rational)
203 {
204    cout <<
“Enter numerator: “;
205    in >> rational.numerator;
206
207    cout <<
“Enter denominator: “;
208    in >> rational.denominator;
209   
return in;
210 }
211

212 // Define function operators for relational operators
213 bool operator<(const Rational& r1, const Rational& r2)
214 {
215   
return r1.compareTo(r2) < 0;
216 }
217
218
bool operator<=(const Rational& r1, const Rational& r2)
219 {
220   
return r1.compareTo(r2) <= 0;
221 }
222
223
bool operator>(const Rational& r1, const Rational& r2)
224 {
225   
return r1.compareTo(r2) > 0;
226 }
227
228
bool operator>=(const Rational& r1, const Rational& r2)
229 {
230   
return r1.compareTo(r2) >= 0;
231 }
232
233
bool operator==(const Rational& r1, const Rational& r2)
234 {
235   
return r1.compareTo(r2) == 0;
236 }
237
238
bool operator!=(const Rational& r1, const Rational& r2)
239 {
240   
return r1.compareTo(r2) != 0;
241 }
242
243
// Define nonmember function operators for arithmetic operators
244 Rational operator+(const Rational& r1, const Rational& r2)
245 {
246   
return r1.add(r2);
247 }
248
249 Rational
operator-(const Rational& r1, const Rational& r2)
250 {
251   
return r1.subtract(r2);
252 }
253
254 Rational
operator*(const Rational& r1, const Rational& r2)
255 {
256   
return r1.multiply(r2);
257 }
258
259 Rational
operator/(const Rational& r1, const Rational& r2)
260 {
261   
return r1.divide(r2);
262 }

Listing 14.9 gives a program for testing the new Rational class

Listing 14.9 TestRationalWithOperators.cpp

1 #include <iostream>
2
#include <string>
3
#include “RationalWithOperators.h”
4 using namespace std;

5
6
int main()
7 {
8     
// Create and initialize two rational numbers r1 and r2.
9     Rational r1(4, 2);
10    Rational r2(
2, 3);
11
12   
// Test relational operators
13    cout << r1 << ” > ” << r2 << ” is ” <<
14       ((r1 > r2) ?
“true” : “false”) << endl;
15    cout << r1 <<
” < ” << r2 << ” is ” <<
16       ((r1 < r2) ?
“true” : “false”) << endl;
17    cout << r1 <<
” == ” << r2 << ” is ” <<
18       ((r1 == r2) ?
“true” : “false”) << endl;
19    cout << r1 <<
” != ” << r2 << ” is ” <<
20       ((r1 != r2) ?
“true” : “false”) << endl;
21
22   
// Test toString, add, subtract, multiply, and divide operators
23    cout << r1 << ” + ” << r2 << ” = ” << r1 + r2 << endl;
24    cout << r1 <<
” – ” << r2 << ” = ” << r1 – r2 << endl;
25    cout << r1 <<
” * ” << r2 << ” = ” << r1 * r2 << endl;
26    cout << r1 <<
” / ” << r2 << ” = ” << r1 / r2 << endl;
27
28   
// Test augmented operators
29    Rational r3(1, 2);
30    r3 += r1;
31    cout <<
“r3 is ” << r3 << endl;
32
33   
// Test function operator [] 34    Rational r4(1, 2);
35    r4[
0] = 3; r4[1] = 4;
36    cout <<
“r4 is ” << r4 << endl;
37
38   
// Test function operators for prefix ++ and —
39    r3 = r4++;
40    cout <<
“r3 is ” << r3 << endl;
41    cout <<
“r4 is ” << r4 << endl;
42
43   
// Test function operator for conversion
44    cout << “1 + ” << r4 << ” is ” << (1 + r4) << endl;
45
46   
return 0;
47 }

Source: Liang Y. Daniel (2013), Introduction to programming with C++, Pearson; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *