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 ambiguity 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. However, 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 function 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 reference. 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 {
9 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 operators +=, -=, *=, 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 invoking r1.compareTo(r2) (lines 213-241). The arithmetic operators +, -, *, and / are implemented 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.