Some of the most commonly used operators in C++ are the arithmetic operators -- that is, the plus operator (+), minus operator (-), multiplication operator (*), and division operator (/). Note that all of the arithmetic operators are binary operators -- meaning they take two operands -- one on each side of the operator. All four of these operators are overloaded in the exact same way.

It turns out that there are three different ways to overload operators: the member function way, the friend function way, and the normal function way. In this lesson, we’ll cover the friend function way (because it’s more intuitive for most binary operators). Next lesson, we’ll discuss the normal function way. Finally, in a later lesson in this chapter, we’ll cover the member function way. And, of course, we’ll also summarize when to use each in more detail.

**Overloading operators using friend functions**

Consider the following trivial class:

1 2 3 4 5 6 7 8 9 |
class Cents { private: int m_cents; public: Cents(int cents) { m_cents = cents; } int getCents() const { return m_cents; } }; |

The following example shows how to overload operator plus (+) in order to add two “Cents” objects together:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <iostream> class Cents { private: int m_cents; public: Cents(int cents) { m_cents = cents; } // add Cents + Cents using a friend function friend Cents operator+(const Cents &c1, const Cents &c2); int getCents() const { return m_cents; } }; // note: this function is not a member function! Cents operator+(const Cents &c1, const Cents &c2) { // use the Cents constructor and operator+(int, int) // we can access m_cents directly because this is a friend function return Cents(c1.m_cents + c2.m_cents); } int main() { Cents cents1(6); Cents cents2(8); Cents centsSum = cents1 + cents2; std::cout << "I have " << centsSum.getCents() << " cents." << std::endl; return 0; } |

This produces the result:

I have 14 cents.

Overloading the plus operator (+) is as simple as declaring a function named operator+, giving it two parameters of the type of the operands we want to add, picking an appropriate return type, and then writing the function.

In the case of our Cents object, implementing our operator+() function is very simple. First, the parameter types: in this version of operator+, we are going to add two Cents objects together, so our function will take two objects of type Cents. Second, the return type: our operator+ is going to return a result of type Cents, so that’s our return type.

Finally, implementation: to add two Cents objects together, we really need to add the m_cents member from each Cents object. Because our overloaded operator+() function is a friend of the class, we can access the m_cents member of our parameters directly. Also, because m_cents is an integer, and C++ knows how to add integers together using the built-in version of the plus operator that works with integer operands, we can simply use the + operator to do the adding.

Overloading the subtraction operator (-) is simple as well:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#include <iostream> class Cents { private: int m_cents; public: Cents(int cents) { m_cents = cents; } // add Cents + Cents using a friend function friend Cents operator+(const Cents &c1, const Cents &c2); // subtract Cents - Cents using a friend function friend Cents operator-(const Cents &c1, const Cents &c2); int getCents() const { return m_cents; } }; // note: this function is not a member function! Cents operator+(const Cents &c1, const Cents &c2) { // use the Cents constructor and operator+(int, int) // we can access m_cents directly because this is a friend function return Cents(c1.m_cents + c2.m_cents); } // note: this function is not a member function! Cents operator-(const Cents &c1, const Cents &c2) { // use the Cents constructor and operator-(int, int) // we can access m_cents directly because this is a friend function return Cents(c1.m_cents - c2.m_cents); } int main() { Cents cents1(6); Cents cents2(2); Cents centsSum = cents1 - cents2; std::cout << "I have " << centsSum.getCents() << " cents." << std::endl; return 0; } |

Overloading the multiplication operator (*) and division operator (/) are as easy as defining functions for operator* and operator/.

**Friend functions can be defined inside the class**

Even though friend functions are not members of the class, they can still be defined inside the class if desired:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include <iostream> class Cents { private: int m_cents; public: Cents(int cents) { m_cents = cents; } // add Cents + Cents using a friend function // This function is not considered a member of the class, even though the definition is inside the class friend Cents operator+(const Cents &c1, const Cents &c2) { // use the Cents constructor and operator+(int, int) // we can access m_cents directly because this is a friend function return Cents(c1.m_cents + c2.m_cents); } int getCents() const { return m_cents; } }; int main() { Cents cents1(6); Cents cents2(8); Cents centsSum = cents1 + cents2; std::cout << "I have " << centsSum.getCents() << " cents." << std::endl; return 0; } |

We generally don’t recommend this, as non-trivial function definitions are better kept in a separate .cpp file, outside of the class definition. However, we will use this pattern in future tutorials to keep the examples concise.

**Overloading operators for operands of different types**

Often it is the case that you want your overloaded operators to work with operands that are different types. For example, if we have Cents(4), we may want to add the integer 6 to this to produce the result Cents(10).

When C++ evaluates the expression `x + y`

, x becomes the first parameter, and y becomes the second parameter. When x and y have the same type, it does not matter if you add x + y or y + x -- either way, the same version of operator+ gets called. However, when the operands have different types, x + y does not call the same function as y + x.

For example, `Cents(4) + 6`

would call operator+(Cents, int), and `6 + Cents(4)`

would call operator+(int, Cents). Consequently, whenever we overload binary operators for operands of different types, we actually need to write two functions -- one for each case. Here is an example of that:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include <iostream> class Cents { private: int m_cents; public: Cents(int cents) { m_cents = cents; } // add Cents + int using a friend function friend Cents operator+(const Cents &c1, int value); // add int + Cents using a friend function friend Cents operator+(int value, const Cents &c1); int getCents() { return m_cents; } }; // note: this function is not a member function! Cents operator+(const Cents &c1, int value) { // use the Cents constructor and operator+(int, int) // we can access m_cents directly because this is a friend function return Cents(c1.m_cents + value); } // note: this function is not a member function! Cents operator+(int value, const Cents &c1) { // use the Cents constructor and operator+(int, int) // we can access m_cents directly because this is a friend function return Cents(c1.m_cents + value); } int main() { Cents c1 = Cents(4) + 6; Cents c2 = 6 + Cents(4); std::cout << "I have " << c1.getCents() << " cents." << std::endl; std::cout << "I have " << c2.getCents() << " cents." << std::endl; return 0; } |

Note that both overloaded functions have the same implementation -- that’s because they do the same thing, they just take their parameters in a different order.

**Another example**

Let’s take a look at another example:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
class MinMax { private: int m_min; // The min value seen so far int m_max; // The max value seen so far public: MinMax(int min, int max) { m_min = min; m_max = max; } int getMin() { return m_min; } int getMax() { return m_max; } friend MinMax operator+(const MinMax &m1, const MinMax &m2); friend MinMax operator+(const MinMax &m, int value); friend MinMax operator+(int value, const MinMax &m); }; MinMax operator+(const MinMax &m1, const MinMax &m2) { // Get the minimum value seen in m1 and m2 int min = m1.m_min < m2.m_min ? m1.m_min : m2.m_min; // Get the maximum value seen in m1 and m2 int max = m1.m_max > m2.m_max ? m1.m_max : m2.m_max; return MinMax(min, max); } MinMax operator+(const MinMax &m, int value) { // Get the minimum value seen in m and value int min = m.m_min < value ? m.m_min : value; // Get the maximum value seen in m and value int max = m.m_max > value ? m.m_max : value; return MinMax(min, max); } MinMax operator+(int value, const MinMax &m) { // call operator+(MinMax, int) return m + value; } int main() { MinMax m1(10, 15); MinMax m2(8, 11); MinMax m3(3, 12); MinMax mFinal = m1 + m2 + 5 + 8 + m3 + 16; std::cout << "Result: (" << mFinal.getMin() << ", " << mFinal.getMax() << ")\n"; return 0; } |

The MinMax class keeps track of the minimum and maximum values that it has seen so far. We have overloaded the + operator 3 times, so that we can add two MinMax objects together, or add integers to MinMax objects.

This example produces the result:

Result: (3, 16)

which you will note is the minimum and maximum values that we added to mFinal.

Let’s talk a little bit more about how “MinMax mFinal = m1 + m2 + 5 + 8 + m3 + 16” evaluates. Remember that operator+ has higher precedence than operator=, and operator+ evaluates from left to right, so m1 + m2 evaluate first. This becomes a call to operator+(m1, m2), which produces the return value MinMax(8, 15). Then MinMax(8, 15) + 5 evaluates next. This becomes a call to operator+(MinMax(8, 15), 5), which produces return value MinMax(5, 15). Then MinMax(5, 15) + 8 evaluates in the same way to produce MinMax(5, 15). Then MinMax(5, 15) + m3 evaluates to produce MinMax(3, 15). And finally, MinMax(3, 15) + 16 evaluates to MinMax(3, 16). This final result is then assigned to mFinal.

In other words, this expression evaluates as “MinMax mFinal = (((((m1 + m2) + 5) + 8) + m3) + 16)”, with each successive operation returning a MinMax object that becomes the left-hand operand for the following operator.

**Implementing operators using other operators**

In the above example, note that we defined operator+(int, MinMax) by calling operator+(MinMax, int) (which produces the same result). This allows us to reduce the implementation of operator+(MinMax, int) to a single line, making our code easier to maintain by minimizing redundancy and making the function simpler to understand.

It is often possible to define overloaded operators by calling other overloaded operators. You should do so if and when doing so produces simpler code. In cases where the implementation is trivial (e.g. a single line) it’s often not worth doing this, as the added indirection of an additional function call is more complicated than just implementing the function directly.

**Quiz time**

1a) Write a class named Fraction that has a integer numerator and denominator member. Write a print() function that prints out the fraction.

The following code should compile:

1 2 3 4 5 6 7 8 9 10 |
#include <iostream> int main() { Fraction f1(1, 4); f1.print(); Fraction f2(1, 2); f2.print(); } |

This should print:

1/4 1/2

1b) Add overloaded multiplication operators to handle multiplication between a Fraction and integer, and between two Fractions. Use the friend function method.

Hint: To multiply two fractions, first multiply the two numerators together, and then multiply the two denominators together. To multiply a fraction and an integer, multiply the numerator of the fraction by the integer and leave the denominator alone.

The following code should compile:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <iostream> int main() { Fraction f1(2, 5); f1.print(); Fraction f2(3, 8); f2.print(); Fraction f3 = f1 * f2; f3.print(); Fraction f4 = f1 * 2; f4.print(); Fraction f5 = 2 * f2; f5.print(); Fraction f6 = Fraction(1, 2) * Fraction(2, 3) * Fraction(3, 4); f6.print(); } |

This should print:

2/5 3/8 6/40 4/5 6/8 6/24

1c) Extra credit: the fraction 2/4 is the same as 1/2, but 2/4 is not reduced to the lowest terms. We can reduce any given fraction to lowest terms by finding the greatest common divisor (GCD) between the numerator and denominator, and then dividing both the numerator and denominator by the GCD.

The following is a function to find the GCD:

1 2 3 |
int gcd(int a, int b) { return (b == 0) ? (a > 0 ? a : -a) : gcd(b, a % b); } |

Add this function to your class, and write a member function named reduce() that reduces your fraction. Make sure all fractions are properly reduced.

The following should compile:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <iostream> int main() { Fraction f1(2, 5); f1.print(); Fraction f2(3, 8); f2.print(); Fraction f3 = f1 * f2; f3.print(); Fraction f4 = f1 * 2; f4.print(); Fraction f5 = 2 * f2; f5.print(); Fraction f6 = Fraction(1, 2) * Fraction(2, 3) * Fraction(3, 4); f6.print(); return 0; } |

And produce the result:

2/5 3/8 3/20 4/5 3/4 1/4

9.2a -- Overloading operators using normal functions |

Index |

9.1 -- Introduction to operator overloading |

I did 1(c) for the extra kudos:

Here's my solution to 1(b):

As both the 2nd and 3rd overload functions have the same return type would it possible to reference one to the other to save code?

Uniform initialization.

I assumed that for the 1c) extra credit part it is forbidden to change main() and include an extra function call.

//this part is not very important

First off, that task made me understand why putting a function in the constructor that calls the same constructor is a bad idea, hello infinite loop.

Now in my first try I implemented reduce() in a way that it takes a Fraction Object AND returns one too, however, in the comments I read that doing so is inefficent because it creates a new Object just to pass the reduced fraction.

This is where I got the loop in the constructor too, so I put it into print(), which worked but it was kind of an awkward place to put it.

Also, when I tried to put it into the constructor or print() function I did not know how to access the object that was passed in main() because it was not passed as an argument within the bracktes, instead the function was used on the Object if I understand correctly (Class::object.Class::method).

//this part is not very important

In the end I rewrote the reduce() function in a way that it just modifies the passed in Object (if I understand my own code correctly, that is) which I pass with (*this).

The code works, but if anyone has any tips on how I could implement this in a different/better way, I'm open to all suggestions.

Hi Synex!

> I rewrote the reduce() function in a way that it just modifies the passed in Object

No it doesn't. It modifies @this, @a is unused. When you're accessing member variables without specifying an object you're accessing the local variables.

* @Fraction::print should be const

* @getGcd doesn't need to be a non-static member function

* Initialize your variables with uniform initialization to prevent accidental implicit casts

* Include <cassert> instead of "assert.h". <*.h> are mostly C headers, <c*> are the C++ versions.

Rest looks fine

Hey man,

I fixed those mistakes now. Thanks for helping me out!

One thing I am not entirely sure about is turning getGcd() into a static member function.

I re-read the 8.12 Chapter on static member functions and it seems the advantage here is that the function only gets instantiated once and is then used by all Objects of Fractions(f1,f2,f3...) without going out of scope and being destroyed inbetween?

* Line 14: You got it the wrong way around

@reduce can not be const, because it modifies members. (Lesson 8.10)

> the function only gets instantiated once and is then used by all Objects of Fractions

That's the case for all functions. static functions can be used without an object

Since @Fraction::gcd doesn't need a @Fraction object it can be declared static. This doesn't change anything, but classes should only declare functions as members if the functions need an object. You might as well move @gcd outside of @Fraction and into a "Math" class or namespace.

> without [...] being destroyed

Functions are never destroyed. They live as long as your program does.

* Line 34, 42, 47, 52, 58, 61, 64, 67, 70, 73: Uniform initialization

Ah, so "const void fnc()" makes "void" constant, which is nonsense and does nothing in this case.

Explains why it still compiled and worked after I mistakenly changed it to "const void reduce()", reduce() was still allowed to change stuff afterall. ^^

Regarding the static functions, I guess it will come to me more naturally once I use more classes (put into their respective files) and reuse a lot of math heavy "helper functions".

I totally did not see the wrong initialization at lines 34,42,47,52, I'm not used to returning stuff that I initialize in the same line. Previously we would always initialize some variables and then return a "sum" variable for example.

Thank you very much for your help!

Line 73 is still using copy initialization

When calling a constructor while returning the to-be-constructed object, you don't need to explicitly name the constructor, as it can be deduced from the return type.

Same for the other functions.

> Line 73 is still using copy initialization

Oha, got it!

> you don't need to explicitly name the constructor, as it can be deduced from the return type.

Fixed the others, makes sense, I think this was also mentioned in a previous chapter, gotta go a little slower I guess.

You clarified some important things for me, gonna try hard and get used to using uniform initialization whenever I can.

Hi Alex,,

I apologize if I am asking a question that has probably been answered before, but I am struggling to understand why you use & before object in many positions as you mentioned before in the quiz part

Hi Gad!

He's passing the arguments by reference. Give lessons 6.11, 6.11a, 7.3 and 7.4a another read.

thanks nascardriver for illustration

but in the same point he used const keyword i know that const objects cant access non const functions, he didnt use any function here so what is his purpose here .

Whenever you write a function that doesn't modify a reference parameter, you should mark the parameter const. That way the function works with both, const, and non-const arguments. If you don't mark the parameter const, you can only pass non-const parameters.

Alex might not have used const object in this case, but you should always think ahead to increase forward compatibility. Otherwise you're going to spent a lot of boring time updating old code once you get there.

thanks a lot nascardriver

I apologize if I am asking a question that has probably been answered before, but I am struggling to understand why

Fraction operator*(const Fraction &f1, const Fraction &f2)

uses a pointer.

Hi Nicholas!

That's not a pointer, that's the multiplication operator.

Hi, based on the code below, what is the purpose of returning Cents(c1.m_cents + c2.m_cents)? Why do we need to cast cents in front of it? Is it because that c1.m_cents+c2.m_cents are integer values so we need to cast them back to Cents type? Also, in main(), how did the "centsum =" update it's own member m_cents by the return value of "Cents(c1.m_cents + c2.m_cents)" when the function operator+ returns it? Thanks so much for your help!

Hi Winston!

> Is it because that c1.m_cents+c2.m_cents are integer values

Yes, but that's not a cast, that's creation of a new @Cents object.

> how did the "centsum =" update it's own member m_cents

It doesn't. @cents1 and @cents2 remain unchanged. A new @Cents object is created by @operator+.

Hi nascardriver,

Thank you so much for your reply!

I am a bit confused here. If "centsum =" doesn't update it's own member m_cents then why would std::cout able to utilize centsSum.getCents() to print out the total number of cents? Since m_cents never get updated for variable centsSum?

@centsSum gets created by @operator+, before being created, it doesn't exists and there is nothing to update.

Hi nascardriver,

Am I interpret this correctly? @centsSum gets created by @operator+, so @centsSum will initiate its own m_cents member which is composed of c1.m_cents+c1.m_cents. None of the m_cents of c1 or c2 has been changed.

correct

Thanks a lot Nascardriver!!

Regarding Q1a:

Shouldn't Fraction::print() be const?

Hi Louis!

It should. You'll often find member function that aren't declared const, although they should be. It's easily forgot when the class isn't used much.

In the MinMax example, you say:

'When C++ evaluates the expression x + y, x becomes the first parameter, and y becomes the second parameter. When x and y have the same type, it does not matter if you add x + y or y + x -- either way, the same version of operator+ gets called. However, when the operands have different types, x + y is not the same as y + x.'

and:

'One other interesting thing to note is that we defined operator+(int, MinMax) by calling operator+(MinMax, int). This may be slightly less efficient than implementing it directly (due to the extra function call, assuming your compiler can’t optimize the function call away), but keeps our code shorter and easier to maintain because it reduces duplicate code. It is often possible to define overloaded operators by calling other overloaded operators -- when possible, do so!'

From the first statement, we know that:

To overload the '*' operator to allow for multiplying a fraction by a fraction, and a fraction by a value, we need 3 different functions:

Now, from the second statement, we get told to overload the operators by calling other overloaded operators when possible. Therefore isn't:

the correct way to solve the problem? In the solution, the entire last function is rewritten?

For ease of discussion:

A = Fraction operator*(const Fraction &f1, int value)

B = Fraction operator*(int value, const Fraction &f1)

If the implementation of A is complex, then it definitely makes sense to implement B using A. However, if A is trivial, then it can be simpler to just implement B directly (as it should also be trivial -- otherwise there's an extra indirection being made). I'll make this caveat clearer in the lesson text.

Quick question:

1. Why doesnt this work?

Either i have to add this->gcd(...) or i have to make the variable a different name.

2. What is in this case best practice with the gcd function regarding static/non-static and private/public?

Hi Donlod!

1. That's the result of a poor naming convention. "gcd" occurs twice but naming different entities. Your compiler can't differentiate between the two.

2. Ideally @gcd shouldn't be a part of @Fraction but rather a part of a more general mathematical class or namespace. If it's in @Fraction, I'd go for private static.

Regarding the extra credit problem, shouldn't be gcd(int, int) and reduce() be private members of the class Fraction? I think, normally, the user don't need access to them in these circumstances, right?

Well, I manage to solve the quizz, but in a not so efficient manner:

- it didn't cross my mind to put reduce() in the constructor, I used instead the following:

paired with:

Not sure how it looks concerning the memory usage [the anonymous object returned by reduce() looks like an unwelcome guest to a party to me now ...].

Fraction.cpp:

Fraction.h

Hi radu!

> shouldn't be gcd(int, int) and reduce() be private members of the class Fraction?

If you don't intend them being used outside the class you should declare them private (or protected).

> Not sure how it looks concerning the memory usage

Bad. You're creating two @Fraction objects although you only need one in the end.

This problem can be solved by having @reduce modify the object it's called on rather than creating a new @Fraction and returning itself by reference or pointer.