Thinking in C++, 2nd ed. Volume 1
©2000 by Bruce Eckel
Unary operators
The following example shows the syntax to overload all the unary operators, in the form of both global functions (non-member friend functions) and as member functions. These will expand upon the Integer class shown previously and add a new byte class. The meaning of your particular operators will depend on the way you want to use them, but consider the client programmer before doing something unexpected.
Here is a catalog of all the unary functions:
1 //: C12:OverloadingUnaryOperators.cpp
2 #include <iostream>
3 using namespace std;
4
5 // Non-member functions:
6 class Integer {
7 long i;
8 Integer* This() { return this; }
9 public:
10 Integer(long ll = 0) : i(ll) {}
11 // No side effects takes const& argument:
12 friend const Integer&
13 operator+(const Integer& a);
14 friend const Integer
15 operator-(const Integer& a);
16 friend const Integer
17 operator~(const Integer& a);
18 friend Integer*
19 operator&(Integer& a);
20 friend int
21 operator!(const Integer& a);
22 // Side effects have non-const& argument:
23 // Prefix:
24 friend const Integer&
25 operator++(Integer& a);
26 // Postfix:
27 friend const Integer
28 operator++(Integer& a, int);
29 // Prefix:
30 friend const Integer&
31 operator--(Integer& a);
32 // Postfix:
33 friend const Integer
34 operator--(Integer& a, int);
35 };
36
37 // Global operators:
38 const Integer& operator+(const Integer& a) {
39 cout << "+Integer\n";
40 return a; // Unary + has no effect
41 }
42 const Integer operator-(const Integer& a) {
43 cout << "-Integer\n";
44 return Integer(-a.i);
45 }
46 const Integer operator~(const Integer& a) {
47 cout << "~Integer\n";
48 return Integer(~a.i);
49 }
50 Integer* operator&(Integer& a) {
51 cout << "&Integer\n";
52 return a.This(); // &a is recursive!
53 }
54 int operator!(const Integer& a) {
55 cout << "!Integer\n";
56 return !a.i;
57 }
58 // Prefix; return incremented value
59 const Integer& operator++(Integer& a) {
60 cout << "++Integer\n";
61 a.i++;
62 return a;
63 }
64 // Postfix; return the value before increment:
65 const Integer operator++(Integer& a, int) {
66 cout << "Integer++\n";
67 Integer before(a.i);
68 a.i++;
69 return before;
70 }
71 // Prefix; return decremented value
72 const Integer& operator--(Integer& a) {
73 cout << "--Integer\n";
74 a.i--;
75 return a;
76 }
77 // Postfix; return the value before decrement:
78 const Integer operator--(Integer& a, int) {
79 cout << "Integer--\n";
80 Integer before(a.i);
81 a.i--;
82 return before;
83 }
84
85 // Show that the overloaded operators work:
86 void f(Integer a) {
87 +a;
88 -a;
89 ~a;
90 Integer* ip = &a;
91 !a;
92 ++a;
93 a++;
94 --a;
95 a--;
96 }
97
98 // Member functions (implicit "this"):
99 class Byte {
100 unsigned char b;
101 public:
102 Byte(unsigned char bb = 0) : b(bb) {}
103 // No side effects: const member function:
104 const Byte& operator+() const {
105 cout << "+Byte\n";
106 return *this;
107 }
108 const Byte operator-() const {
109 cout << "-Byte\n";
110 return Byte(-b);
111 }
112 const Byte operator~() const {
113 cout << "~Byte\n";
114 return Byte(~b);
115 }
116 Byte operator!() const {
117 cout << "!Byte\n";
118 return Byte(!b);
119 }
120 Byte* operator&() {
121 cout << "&Byte\n";
122 return this;
123 }
124 // Side effects: non-const member function:
125 const Byte& operator++() { // Prefix
126 cout << "++Byte\n";
127 b++;
128 return *this;
129 }
130 const Byte operator++(int) { // Postfix
131 cout << "Byte++\n";
132 Byte before(b);
133 b++;
134 return before;
135 }
136 const Byte& operator--() { // Prefix
137 cout << "--Byte\n";
138 --b;
139 return *this;
140 }
141 const Byte operator--(int) { // Postfix
142 cout << "Byte--\n";
143 Byte before(b);
144 --b;
145 return before;
146 }
147 };
148
149 void g(Byte b) {
150 +b;
151 -b;
152 ~b;
153 Byte* bp = &b;
154 !b;
155 ++b;
156 b++;
157 --b;
158 b--;
159 }
160
161 int main() {
162 Integer a;
163 f(a);
164 Byte b;
165 g(b);
166 } ///:~
167
The functions are grouped according to the way their arguments are passed. Guidelines for how to pass and return arguments are given later. The forms above (and the ones that follow in the next section) are typically what you’ll use, so start with them as a pattern when overloading your own operators.
Binary operators
The following listing repeats the example of OverloadingUnaryOperators.cpp for binary operators so you have an example of all the operators you might want to overload. Again, both global versions and member function versions are shown.
1 //: C12:Integer.h
2 // Non-member overloaded operators
3 #ifndef INTEGER_H
4 #define INTEGER_H
5 #include <iostream>
6
7 // Non-member functions:
8 class Integer {
9 long i;
10 public:
11 Integer(long ll = 0) : i(ll) {}
12 // Operators that create new, modified value:
13 friend const Integer
14 operator+(const Integer& left,
15 const Integer& right);
16 friend const Integer
17 operator-(const Integer& left,
18 const Integer& right);
19 friend const Integer
20 operator*(const Integer& left,
21 const Integer& right);
22 friend const Integer
23 operator/(const Integer& left,
24 const Integer& right);
25 friend const Integer
26 operator%(const Integer& left,
27 const Integer& right);
28 friend const Integer
29 operator^(const Integer& left,
30 const Integer& right);
31 friend const Integer
32 operator&(const Integer& left,
33 const Integer& right);
34 friend const Integer
35 operator|(const Integer& left,
36 const Integer& right);
37 friend const Integer
38 operator<<(const Integer& left,
39 const Integer& right);
40 friend const Integer
41 operator>>(const Integer& left,
42 const Integer& right);
43 // Assignments modify & return lvalue:
44 friend Integer&
45 operator+=(Integer& left,
46 const Integer& right);
47 friend Integer&
48 operator-=(Integer& left,
49 const Integer& right);
50 friend Integer&
51 operator*=(Integer& left,
52 const Integer& right);
53 friend Integer&
54 operator/=(Integer& left,
55 const Integer& right);
56 friend Integer&
57 operator%=(Integer& left,
58 const Integer& right);
59 friend Integer&
60 operator^=(Integer& left,
61 const Integer& right);
62 friend Integer&
63 operator&=(Integer& left,
64 const Integer& right);
65 friend Integer&
66 operator|=(Integer& left,
67 const Integer& right);
68 friend Integer&
69 operator>>=(Integer& left,
70 const Integer& right);
71 friend Integer&
72 operator<<=(Integer& left,
73 const Integer& right);
74 // Conditional operators return true/false:
75 friend int
76 operator==(const Integer& left,
77 const Integer& right);
78 friend int
79 operator!=(const Integer& left,
80 const Integer& right);
81 friend int
82 operator<(const Integer& left,
83 const Integer& right);
84 friend int
85 operator>(const Integer& left,
86 const Integer& right);
87 friend int
88 operator<=(const Integer& left,
89 const Integer& right);
90 friend int
91 operator>=(const Integer& left,
92 const Integer& right);
93 friend int
94 operator&&(const Integer& left,
95 const Integer& right);
96 friend int
97 operator||(const Integer& left,
98 const Integer& right);
99 // Write the contents to an ostream:
100 void print(std::ostream& os) const { os << i; }
101 };
102 #endif // INTEGER_H ///:~
103 //: C12:Integer.cpp {O}
104 // Implementation of overloaded operators
105 #include "Integer.h"
106 #include "../require.h"
107
108 const Integer
109 operator+(const Integer& left,
110 const Integer& right) {
111 return Integer(left.i + right.i);
112 }
113 const Integer
114 operator-(const Integer& left,
115 const Integer& right) {
116 return Integer(left.i - right.i);
117 }
118 const Integer
119 operator*(const Integer& left,
120 const Integer& right) {
121 return Integer(left.i * right.i);
122 }
123 const Integer
124 operator/(const Integer& left,
125 const Integer& right) {
126 require(right.i != 0, "divide by zero");
127 return Integer(left.i / right.i);
128 }
129 const Integer
130 operator%(const Integer& left,
131 const Integer& right) {
132 require(right.i != 0, "modulo by zero");
133 return Integer(left.i % right.i);
134 }
135 const Integer
136 operator^(const Integer& left,
137 const Integer& right) {
138 return Integer(left.i ^ right.i);
139 }
140 const Integer
141 operator&(const Integer& left,
142 const Integer& right) {
143 return Integer(left.i & right.i);
144 }
145 const Integer
146 operator|(const Integer& left,
147 const Integer& right) {
148 return Integer(left.i | right.i);
149 }
150 const Integer
151 operator<<(const Integer& left,
152 const Integer& right) {
153 return Integer(left.i << right.i);
154 }
155 const Integer
156 operator>>(const Integer& left,
157 const Integer& right) {
158 return Integer(left.i >> right.i);
159 }
160 // Assignments modify & return lvalue:
161 Integer& operator+=(Integer& left,
162 const Integer& right) {
163 if(&left == &right) {/* self-assignment */}
164 left.i += right.i;
165 return left;
166 }
167 Integer& operator-=(Integer& left,
168 const Integer& right) {
169 if(&left == &right) {/* self-assignment */}
170 left.i -= right.i;
171 return left;
172 }
173 Integer& operator*=(Integer& left,
174 const Integer& right) {
175 if(&left == &right) {/* self-assignment */}
176 left.i *= right.i;
177 return left;
178 }
179 Integer& operator/=(Integer& left,
180 const Integer& right) {
181 require(right.i != 0, "divide by zero");
182 if(&left == &right) {/* self-assignment */}
183 left.i /= right.i;
184 return left;
185 }
186 Integer& operator%=(Integer& left,
187 const Integer& right) {
188 require(right.i != 0, "modulo by zero");
189 if(&left == &right) {/* self-assignment */}
190 left.i %= right.i;
191 return left;
192 }
193 Integer& operator^=(Integer& left,
194 const Integer& right) {
195 if(&left == &right) {/* self-assignment */}
196 left.i ^= right.i;
197 return left;
198 }
199 Integer& operator&=(Integer& left,
200 const Integer& right) {
201 if(&left == &right) {/* self-assignment */}
202 left.i &= right.i;
203 return left;
204 }
205 Integer& operator|=(Integer& left,
206 const Integer& right) {
207 if(&left == &right) {/* self-assignment */}
208 left.i |= right.i;
209 return left;
210 }
211 Integer& operator>>=(Integer& left,
212 const Integer& right) {
213 if(&left == &right) {/* self-assignment */}
214 left.i >>= right.i;
215 return left;
216 }
217 Integer& operator<<=(Integer& left,
218 const Integer& right) {
219 if(&left == &right) {/* self-assignment */}
220 left.i <<= right.i;
221 return left;
222 }
223 // Conditional operators return true/false:
224 int operator==(const Integer& left,
225 const Integer& right) {
226 return left.i == right.i;
227 }
228 int operator!=(const Integer& left,
229 const Integer& right) {
230 return left.i != right.i;
231 }
232 int operator<(const Integer& left,
233 const Integer& right) {
234 return left.i < right.i;
235 }
236 int operator>(const Integer& left,
237 const Integer& right) {
238 return left.i > right.i;
239 }
240 int operator<=(const Integer& left,
241 const Integer& right) {
242 return left.i <= right.i;
243 }
244 int operator>=(const Integer& left,
245 const Integer& right) {
246 return left.i >= right.i;
247 }
248 int operator&&(const Integer& left,
249 const Integer& right) {
250 return left.i && right.i;
251 }
252 int operator||(const Integer& left,
253 const Integer& right) {
254 return left.i || right.i;
255 } ///:~
256 //: C12:IntegerTest.cpp
257 //{L} Integer
258 #include "Integer.h"
259 #include <fstream>
260 using namespace std;
261 ofstream out("IntegerTest.out");
262
263 void h(Integer& c1, Integer& c2) {
264 // A complex expression:
265 c1 += c1 * c2 + c2 % c1;
266 #define TRY(OP) \
267 out << "c1 = "; c1.print(out); \
268 out << ", c2 = "; c2.print(out); \
269 out << "; c1 " #OP " c2 produces "; \
270 (c1 OP c2).print(out); \
271 out << endl;
272 TRY(+) TRY(-) TRY(*) TRY(/)
273 TRY(%) TRY(^) TRY(&) TRY(|)
274 TRY(<<) TRY(>>) TRY(+=) TRY(-=)
275 TRY(*=) TRY(/=) TRY(%=) TRY(^=)
276 TRY(&=) TRY(|=) TRY(>>=) TRY(<<=)
277 // Conditionals:
278 #define TRYC(OP) \
279 out << "c1 = "; c1.print(out); \
280 out << ", c2 = "; c2.print(out); \
281 out << "; c1 " #OP " c2 produces "; \
282 out << (c1 OP c2); \
283 out << endl;
284 TRYC(<) TRYC(>) TRYC(==) TRYC(!=) TRYC(<=)
285 TRYC(>=) TRYC(&&) TRYC(||)
286 }
287
288 int main() {
289 cout << "friend functions" << endl;
290 Integer c1(47), c2(9);
291 h(c1, c2);
292 } ///:~
293 //: C12:Byte.h
294 // Member overloaded operators
295 #ifndef BYTE_H
296 #define BYTE_H
297 #include "../require.h"
298 #include <iostream>
299 // Member functions (implicit "this"):
300 class Byte {
301 unsigned char b;
302 public:
303 Byte(unsigned char bb = 0) : b(bb) {}
304 // No side effects: const member function:
305 const Byte
306 operator+(const Byte& right) const {
307 return Byte(b + right.b);
308 }
309 const Byte
310 operator-(const Byte& right) const {
311 return Byte(b - right.b);
312 }
313 const Byte
314 operator*(const Byte& right) const {
315 return Byte(b * right.b);
316 }
317 const Byte
318 operator/(const Byte& right) const {
319 require(right.b != 0, "divide by zero");
320 return Byte(b / right.b);
321 }
322 const Byte
323 operator%(const Byte& right) const {
324 require(right.b != 0, "modulo by zero");
325 return Byte(b % right.b);
326 }
327 const Byte
328 operator^(const Byte& right) const {
329 return Byte(b ^ right.b);
330 }
331 const Byte
332 operator&(const Byte& right) const {
333 return Byte(b & right.b);
334 }
335 const Byte
336 operator|(const Byte& right) const {
337 return Byte(b | right.b);
338 }
339 const Byte
340 operator<<(const Byte& right) const {
341 return Byte(b << right.b);
342 }
343 const Byte
344 operator>>(const Byte& right) const {
345 return Byte(b >> right.b);
346 }
347 // Assignments modify & return lvalue.
348 // operator= can only be a member function:
349 Byte& operator=(const Byte& right) {
350 // Handle self-assignment:
351 if(this == &right) return *this;
352 b = right.b;
353 return *this;
354 }
355 Byte& operator+=(const Byte& right) {
356 if(this == &right) {/* self-assignment */}
357 b += right.b;
358 return *this;
359 }
360 Byte& operator-=(const Byte& right) {
361 if(this == &right) {/* self-assignment */}
362 b -= right.b;
363 return *this;
364 }
365 Byte& operator*=(const Byte& right) {
366 if(this == &right) {/* self-assignment */}
367 b *= right.b;
368 return *this;
369 }
370 Byte& operator/=(const Byte& right) {
371 require(right.b != 0, "divide by zero");
372 if(this == &right) {/* self-assignment */}
373 b /= right.b;
374 return *this;
375 }
376 Byte& operator%=(const Byte& right) {
377 require(right.b != 0, "modulo by zero");
378 if(this == &right) {/* self-assignment */}
379 b %= right.b;
380 return *this;
381 }
382 Byte& operator^=(const Byte& right) {
383 if(this == &right) {/* self-assignment */}
384 b ^= right.b;
385 return *this;
386 }
387 Byte& operator&=(const Byte& right) {
388 if(this == &right) {/* self-assignment */}
389 b &= right.b;
390 return *this;
391 }
392 Byte& operator|=(const Byte& right) {
393 if(this == &right) {/* self-assignment */}
394 b |= right.b;
395 return *this;
396 }
397 Byte& operator>>=(const Byte& right) {
398 if(this == &right) {/* self-assignment */}
399 b >>= right.b;
400 return *this;
401 }
402 Byte& operator<<=(const Byte& right) {
403 if(this == &right) {/* self-assignment */}
404 b <<= right.b;
405 return *this;
406 }
407 // Conditional operators return true/false:
408 int operator==(const Byte& right) const {
409 return b == right.b;
410 }
411 int operator!=(const Byte& right) const {
412 return b != right.b;
413 }
414 int operator<(const Byte& right) const {
415 return b < right.b;
416 }
417 int operator>(const Byte& right) const {
418 return b > right.b;
419 }
420 int operator<=(const Byte& right) const {
421 return b <= right.b;
422 }
423 int operator>=(const Byte& right) const {
424 return b >= right.b;
425 }
426 int operator&&(const Byte& right) const {
427 return b && right.b;
428 }
429 int operator||(const Byte& right) const {
430 return b || right.b;
431 }
432 // Write the contents to an ostream:
433 void print(std::ostream& os) const {
434 os << "0x" << std::hex << int(b) << std::dec;
435 }
436 };
437 #endif // BYTE_H ///:~
438 //: C12:ByteTest.cpp
439 #include "Byte.h"
440 #include <fstream>
441 using namespace std;
442 ofstream out("ByteTest.out");
443
444 void k(Byte& b1, Byte& b2) {
445 b1 = b1 * b2 + b2 % b1;
446
447 #define TRY2(OP) \
448 out << "b1 = "; b1.print(out); \
449 out << ", b2 = "; b2.print(out); \
450 out << "; b1 " #OP " b2 produces "; \
451 (b1 OP b2).print(out); \
452 out << endl;
453
454 b1 = 9; b2 = 47;
455 TRY2(+) TRY2(-) TRY2(*) TRY2(/)
456 TRY2(%) TRY2(^) TRY2(&) TRY2(|)
457 TRY2(<<) TRY2(>>) TRY2(+=) TRY2(-=)
458 TRY2(*=) TRY2(/=) TRY2(%=) TRY2(^=)
459 TRY2(&=) TRY2(|=) TRY2(>>=) TRY2(<<=)
460 TRY2(=) // Assignment operator
461
462 // Conditionals:
463 #define TRYC2(OP) \
464 out << "b1 = "; b1.print(out); \
465 out << ", b2 = "; b2.print(out); \
466 out << "; b1 " #OP " b2 produces "; \
467 out << (b1 OP b2); \
468 out << endl;
469
470 b1 = 9; b2 = 47;
471 TRYC2(<) TRYC2(>) TRYC2(==) TRYC2(!=) TRYC2(<=)
472 TRYC2(>=) TRYC2(&&) TRYC2(||)
473
474 // Chained assignment:
475 Byte b3 = 92;
476 b1 = b2 = b3;
477 }
478
479 int main() {
480 out << "member functions:" << endl;
481 Byte b1(47), b2(9);
482 k(b1, b2);
483 } ///:~
484
You can see that operator= is only allowed to be a member function. This is explained later.
Notice that all of the assignment operators have code to check for self-assignment;
this is a general guideline. In some cases this is not necessary; for example, with operator+= you often want to say A+=A and have it add A to itself. The most important place to check for self-assignment is operator= because with complicated objects disastrous results may occur. (In some cases it’s OK, but you should always keep it in mind when writing operator=.)
All of the operators shown in the previous two examples are overloaded to handle a single type. It’s also possible to overload operators to handle mixed types, so you can add apples to oranges, for example. Before you start on an exhaustive overloading of operators, however, you should look at the section on automatic type conversion later in this chapter. Often, a type conversion in the right place can save you a lot of overloaded operators.