Forum rules
Please read our Guide to Making Ozzu Tutorials if you would like to submit your own tutorials.
TUTORIAL: Defining and Overloading Operators in C++
Introduction
Note: This tutorial assumes that you already have a moderate understanding of the C++ language. You should be able to write and compile simple programs, and you should be familiar with object-oriented programming and how classes work in C++.
Note: The complete source code for the examples in this tutorial can be downloaded at the end of the tutorial.
One of the powerful features of C++ is the ability to overload operators. Almost every operator in C++ can be overloaded to provide custom functionality that will give your classes greater flexibility and allow then to behave as other programmers would expect them to.
In this tutorial, you will learn what operators are, how they can be overloaded, and some common reasons for overloading various operators.
What is an operator in C++?
Operators are functions. Specifically, operators are special functions that are used to perform operations on data without directly calling methods each time. Common operators that you should be familiar with include +, -, *, /, <<, >>, and so on. All such operators can be used on primative data types in C++ to achieve certain results. For example, we can use the + operator to add together two numeric values, such as ints, floats, and doubles:
In the code above, two operators are used: the = operator and the + operator. The + operator adds together the two integer values, returning the result. The = operator takes the resulting value on the right and assigns it to the variable on the left.
We also see operators used in other areas, such as writing formatted data to a stream:
In the code above, the insertion operator is used to insert data into the output stream.
It is important to note that there are two important types of operators: unary and binary.
Unary operators are operators that act on a single piece of data. Look at the following code:
In the above example, operator ++ and operator -- are unary operators, because they function on only one piece of data, in this case, the int variable myInt.
Binary operators are operators that act on a two pieces of data. Look at the following code:
In the above example, operator / and operator + are binary operators, because they function on two pieces of data, in this case, the float variables numerator and denominator.
What operators can be overloaded?
The following operators can be overloaded:
Source: Wikipedia
Why would I want to overload an operator?
Operators are often overloaded to allow custom types to behave like and be used like primative types. It gives your classes a lot of flexibility in terms what how they interact with the rest of your program. In a minute, we'll take a look at an example class and how we can use overloaded operators to enhance it's functionality.
Member vs. Non-member operators
Before we dig in, it is important to note that there are two groups of operators in terms of how they are implemented: member operators and non-member operators. The distinction between the two is the same as it is with methods and functions.
Member operators are operators that are implemented as member functions (methods) of a class.
Non-member operators are operators that are implemented as regular, non-member functions.
Some operators are required to be member operators, others must be non-member operators, and some can be both.
A basic example: Fraction
For the rest of this tutorial, we will be working with a custom class: Fraction. The class declaration for fraction looks like this:
Fraction is a simple class that represents a number as a fraction containing a numerator and a denominator. These are set via the constructor as seen in the declaration above. This tutorial assumes basic knowledge of C++ and classes, so I won't show the definitions for the constructor and the print method. You can assume that print() will output the fraction in the form "2/5".
To create a new Fraction object, we would write the following:
To print the fraction to standard output, we would write:
As it stands right now, our Fraction class isn't very useful. There's no way to change the value of our fraction once it's been created, thus it will always be used as a constant. In a moment, we will fix this.
Syntax
The syntax for overloading operators is the same as it is for declaring a function or method:
return-type operator operator-to-overload ( parameters );
Since operators are functions, they can in essence be used in their functional form. For example:
would do exactly the same thing as
Obviously, we would never write the first form when writing programs, since it defeats the entire point of using operators in the first place. However, it is important to understand how the compiler treats operators so that we can more easily overload them.
Defining and overloading + and +=
The first operator we want to overload is the + operator. We want to be able to add other fractions to each other. In this case, we can use a member operator, so lets add its declaration to our class:
Let's look at each piece of that declaration individually:
Fraction is the return type for this method. We are returning a new Fraction object containing the result of the addition operation. We do this to allow operator chaining, which allows us to write something like this:
In the code above, the program will add a + b and return a new Fraction object containing the result. The result is then added with c, and the result of that operation is then assigned to d.
operator+ signifies the operator to be overloaded. Note that operator is a keyword in C++; whitespace between it and the actual operator to overload is optional.
const Fraction& other is the single parameter for this method. We take a constant reference to a Function object, because we don't want to ever modify the other Fraction, only the one being added to. We use a Fraction reference rather than a Fraction to avoid making an unnecessary copy each time we add.
Now let's take a look at the definition for this method:
In the method, we find the LCM of the two denominators, and add in the appropriate values from the other function. As mentioned earlier, we return a reference to the current Fraction to allow chaining.
So now we have a way of adding two Fractions together. Fantastic! But wait, what if we want to add whole numbers to our Fractions? It's mighty inconvenient to have to create a new Fraction every time we want to add an integer amount to our Fraction objects. Let's go ahead and add another version of the + operator.
Add the declaration:
And define the method:
Now that we've overloaded the + operator with another version accepting an integer, we are able to write things like this:
At this point, we can easily add the += operator to our class. Let's add the declarations to our class:
Notice that the declarations for += look very similar to +. Let's look at a few key differences.
This time, our return type is Fraction&, a reference to a Fraction object. We use this because the result of the += operation should be a reference back to the original object being added to. Again, this is to allow operator chaining. Consider the following code:
In the code above, the value of c is added to the value of b. The new value of b is then added to the value of a. Returning references to the objects in the += operator allows us to modify each one in the chain as needed.
We also do not declare these methods as const, since they will directly be modifying their objects.
Now lets look at the code for our += operators:
In this implementation, very little code is required since we can use the + operator that we previously defined. The same is true for our "integer version":
Now, we can write things like the following:
Taking things a step further: <<
Now that you're comfortable with implementing some basic member operators, lets take a look at an example of an operator that must be implemented as a non-member function: <<
The << operator is used for two things in C++: to perform a bitwise left shift, and to insert data into a output stream. We will only be dealing with the latter.
At the moment, we have a way of printing our class to an output stream: print(). We pass in an output stream and the fraction gets printed for us. For example:
Will output the following on standard output:
This is fine for minimal usage of the class. But what if we wanted to include our Fraction as part of a more complicated formatted insertion into a stream?
If we want to be able to insert Fractions into streams in this manner, we must define the << operator, or insertion operator. Let's go ahead and declare it in our class:
But wait! The declaration for this function is outside of our class, that is, it's a non-member function! In fact, this operator must be operated as a non-member function. The reason is relatively straightforward once you understand how the operator is called. Remember that a line like the following:
is actually called like this:
To write the << operator as a method of Fraction, the output stream would have to be the single argument to the method, and the Fraction object would have to be on the left hand side, like this:
This is obviously incorrect, since the output stream must always be on the left hand side of the insertion operator. Thus, << would have to be implemented as a member function of std::ostream. Since we cannot modify standard library classes, our only option is to implement << as a non-member function taking two arguments.
Let's look at the code for operator <<:
This implementation seems valid, but there's a problem. Numerator and denominator are private members of Fraction. Because of this, non-member functions such as our << operator do not have access to them. We could write accessors for the data members, however this is unnecessary and duplicates code. Looking back at our class declaration, we see that we already have a nice method to print out a Fraction to an output stream: print(). Since this method is public, let's go ahead and use it in our << operator:
Now we have one single piece of code, contained in our class, that determines how Fractions are displayed. If we ever want to change this, we simply change it in print(), and our << operator will sill work correctly.
Style and usage guidelines
One last thing to address on the subject of operator overloading is style and usage. It can be very tempting for beginners to start overloading all sorts of operators in their classes, sometimes for purposes not intended by that operator. For example, one might be creating a List class and choose to overload the increment operator (++) to automatically add an element to the list. This can be done, and yes, it will work. But it doesn't make sense. The ++ operator is not meant for this purpose, and even though it may seem convenient, this type of functionality should be avoided.
Conclusion
You should now have a solid understanding of how operators work in C++ and how they can be defined and overloaded to allow your classes to function more like primative data types. You should understand the different between unary and binary operators, and well as when to implement operators as member functions versus non-member functions.
I always welcome questions or feedback about this tutorial. Simply post a reply or PM me; I'm glad to help!
Fraction.zip
Note: This tutorial assumes that you already have a moderate understanding of the C++ language. You should be able to write and compile simple programs, and you should be familiar with object-oriented programming and how classes work in C++.
Note: The complete source code for the examples in this tutorial can be downloaded at the end of the tutorial.
One of the powerful features of C++ is the ability to overload operators. Almost every operator in C++ can be overloaded to provide custom functionality that will give your classes greater flexibility and allow then to behave as other programmers would expect them to.
In this tutorial, you will learn what operators are, how they can be overloaded, and some common reasons for overloading various operators.
What is an operator in C++?
Operators are functions. Specifically, operators are special functions that are used to perform operations on data without directly calling methods each time. Common operators that you should be familiar with include +, -, *, /, <<, >>, and so on. All such operators can be used on primative data types in C++ to achieve certain results. For example, we can use the + operator to add together two numeric values, such as ints, floats, and doubles:
In the code above, two operators are used: the = operator and the + operator. The + operator adds together the two integer values, returning the result. The = operator takes the resulting value on the right and assigns it to the variable on the left.
We also see operators used in other areas, such as writing formatted data to a stream:
In the code above, the insertion operator is used to insert data into the output stream.
It is important to note that there are two important types of operators: unary and binary.
Unary operators are operators that act on a single piece of data. Look at the following code:
Code: Select all
- int myInt = 5;
- myInt++; // ++ is a unary operator
- myInt--; // -- is also a unary operator
In the above example, operator ++ and operator -- are unary operators, because they function on only one piece of data, in this case, the int variable myInt.
Binary operators are operators that act on a two pieces of data. Look at the following code:
Code: Select all
- float numerator = 5.376;
- float denominator = 55.372;
- float result = numerator / denominator; // "/" is a binary operator
- result = denominator + numerator; // + is also a binary operator
In the above example, operator / and operator + are binary operators, because they function on two pieces of data, in this case, the float variables numerator and denominator.
What operators can be overloaded?
The following operators can be overloaded:
Source: Wikipedia
- + Unary Plus
+ Addition (Sum)
++ Prefix Increment
++ Postfix Increment
+= Assignment by Addition
- Unary Minus (Negation)
- Subtraction (Difference)
-- Prefix Decrement
-- Postfix Decrement
-= Assignment by Subtraction
* Multiplication (Product)
*= Assignment by Multiplication
/ Division (Quotient)
/= Assignment by Division
% Modulus (Remainder)
%= Assignment by Modulus
< Less Than
<= Less Than or Equal To
> Greater Than
>= Greater Than or Equal To
!= Not Equal To
== Equal To
! Logical Negation
&& Logical AND
|| Logical OR
<< Bitwise Left Shift
<<= Assignment by Bitwise Left Shift
>> Bitwise Right Shift
>>= Assignment by Bitwise Right Shift
~ Bitwise One's Complement
& Bitwise AND
&= Assignment by Bitwise AND
| Bitwise OR
|= Assignment by Bitwise OR
^ Bitwise XOR
^= Assignment by Bitwise XOR
= Basic Assignment
() Function Call
[] Array Subscript
* Indirection (Dereference)
& Address-of (Reference)
-> Member by Pointer
->* Member by Pointer Indirection
(type) Cast
, Comma
sizeof Size-of
typeid Type Identification
new Allocate Storage
new Allocate Storage (Array)
delete Deallocate Storage
delete Deallocate Storage (Array)
Why would I want to overload an operator?
Operators are often overloaded to allow custom types to behave like and be used like primative types. It gives your classes a lot of flexibility in terms what how they interact with the rest of your program. In a minute, we'll take a look at an example class and how we can use overloaded operators to enhance it's functionality.
Member vs. Non-member operators
Before we dig in, it is important to note that there are two groups of operators in terms of how they are implemented: member operators and non-member operators. The distinction between the two is the same as it is with methods and functions.
Member operators are operators that are implemented as member functions (methods) of a class.
Non-member operators are operators that are implemented as regular, non-member functions.
Some operators are required to be member operators, others must be non-member operators, and some can be both.
A basic example: Fraction
For the rest of this tutorial, we will be working with a custom class: Fraction. The class declaration for fraction looks like this:
Code: Select all
- #include <iostream>
- class Fraction {
- private:
- int numerator;
- int denominator;
- public:
- Fraction( int num, int denom );
- void print( const std::ostream& os ) const;
- };
Fraction is a simple class that represents a number as a fraction containing a numerator and a denominator. These are set via the constructor as seen in the declaration above. This tutorial assumes basic knowledge of C++ and classes, so I won't show the definitions for the constructor and the print method. You can assume that print() will output the fraction in the form "2/5".
To create a new Fraction object, we would write the following:
To print the fraction to standard output, we would write:
As it stands right now, our Fraction class isn't very useful. There's no way to change the value of our fraction once it's been created, thus it will always be used as a constant. In a moment, we will fix this.
Syntax
The syntax for overloading operators is the same as it is for declaring a function or method:
return-type operator operator-to-overload ( parameters );
Since operators are functions, they can in essence be used in their functional form. For example:
would do exactly the same thing as
Obviously, we would never write the first form when writing programs, since it defeats the entire point of using operators in the first place. However, it is important to understand how the compiler treats operators so that we can more easily overload them.
Defining and overloading + and +=
The first operator we want to overload is the + operator. We want to be able to add other fractions to each other. In this case, we can use a member operator, so lets add its declaration to our class:
Code: Select all
- #include <iostream>
- class Fraction {
- private:
- int numerator;
- int denominator;
- public:
- Fraction( int num, int denom );
- void print( const std::ostream& os ) const;
- Fraction operator+( const Fraction& other ) const;
- };
Let's look at each piece of that declaration individually:
Fraction is the return type for this method. We are returning a new Fraction object containing the result of the addition operation. We do this to allow operator chaining, which allows us to write something like this:
Code: Select all
- // Declare four fractions
- Fraction a(1, 2), b(2, 5), c(2, 3), d(8, 9);
- // Add a, b, and c together and assign to d
- // Operator chaining allows us to write a + b + c
- d = a + b + c;
In the code above, the program will add a + b and return a new Fraction object containing the result. The result is then added with c, and the result of that operation is then assigned to d.
operator+ signifies the operator to be overloaded. Note that operator is a keyword in C++; whitespace between it and the actual operator to overload is optional.
const Fraction& other is the single parameter for this method. We take a constant reference to a Function object, because we don't want to ever modify the other Fraction, only the one being added to. We use a Fraction reference rather than a Fraction to avoid making an unnecessary copy each time we add.
Now let's take a look at the definition for this method:
Code: Select all
- Fraction Fraction::operator+( const Fraction& other ) const {
- // Find the least common multiple of the denominators
- int lcm = other.denominator;
- while( lcm % denominator != 0 ) {
- lcm += other.denominator;
- }
- // Return a new fraction with the results
- return Fraction(
- (lcm / denominator) * numerator +
- (lcm / other.denominator) * other.numerator, lcm );
- }
In the method, we find the LCM of the two denominators, and add in the appropriate values from the other function. As mentioned earlier, we return a reference to the current Fraction to allow chaining.
So now we have a way of adding two Fractions together. Fantastic! But wait, what if we want to add whole numbers to our Fractions? It's mighty inconvenient to have to create a new Fraction every time we want to add an integer amount to our Fraction objects. Let's go ahead and add another version of the + operator.
Add the declaration:
Code: Select all
- #include <iostream>
- class Fraction {
- private:
- int numerator;
- int denominator;
- public:
- Fraction( int num, int denom );
- void print( const std::ostream& os ) const;
- Fraction operator+( const Fraction& other ) const;
- Fraction operator+( int num ) const;
- };
And define the method:
Code: Select all
- Fraction Fraction::operator+( int num ) const {
- // Return a new fraction with the results
- return Fraction( numerator + denominator * num, denominator );
- }
Now that we've overloaded the + operator with another version accepting an integer, we are able to write things like this:
Code: Select all
- Fraction f( 3, 10 ); // Create the fraction "3/20"
- f = f + 5 // f is now "53/10"
At this point, we can easily add the += operator to our class. Let's add the declarations to our class:
Code: Select all
- #include <iostream>
- class Fraction {
- private:
- int numerator;
- int denominator;
- public:
- Fraction( int num, int denom );
- void print( const std::ostream& os );
- Fraction operator+( const Fraction& other ) const;
- Fraction operator+( int val ) const;
- Fraction& operator+=( const Fraction& other ) const;
- Fraction& operator+=( int val ) const;
- };
Notice that the declarations for += look very similar to +. Let's look at a few key differences.
This time, our return type is Fraction&, a reference to a Fraction object. We use this because the result of the += operation should be a reference back to the original object being added to. Again, this is to allow operator chaining. Consider the following code:
In the code above, the value of c is added to the value of b. The new value of b is then added to the value of a. Returning references to the objects in the += operator allows us to modify each one in the chain as needed.
We also do not declare these methods as const, since they will directly be modifying their objects.
Now lets look at the code for our += operators:
Code: Select all
- Fraction& Fraction::operator+=( const Fraction& other ) {
- // Use the + operator we created
- Fraction temp( *this );
- temp = temp + other;
- numerator = temp.numerator;
- denominator = temp.denominator;
- // Return a reference to ourself
- return *this;
- }
In this implementation, very little code is required since we can use the + operator that we previously defined. The same is true for our "integer version":
Code: Select all
- Fraction& Fraction::operator+=( int val ) {
- // Use the + operator we created
- Fraction temp( *this );
- temp = temp + val;
- numerator = temp.numerator;
- denominator = temp.denominator;
- // Return a reference to ourself
- return *this;
- }
Now, we can write things like the following:
Code: Select all
- Fraction a(3, 5);
- a += a; // Add the value of a to a (double it)
- a += 4; // Add 4 to a
Taking things a step further: <<
Now that you're comfortable with implementing some basic member operators, lets take a look at an example of an operator that must be implemented as a non-member function: <<
The << operator is used for two things in C++: to perform a bitwise left shift, and to insert data into a output stream. We will only be dealing with the latter.
At the moment, we have a way of printing our class to an output stream: print(). We pass in an output stream and the fraction gets printed for us. For example:
Will output the following on standard output:
This is fine for minimal usage of the class. But what if we wanted to include our Fraction as part of a more complicated formatted insertion into a stream?
Code: Select all
- Fraction f(2, 5);
- std::cout << "A string here " << f << " another string, etc." << std::endl;
If we want to be able to insert Fractions into streams in this manner, we must define the << operator, or insertion operator. Let's go ahead and declare it in our class:
Code: Select all
- #include <iostream>
- class Fraction {
- private:
- int numerator;
- int denominator;
- public:
- Fraction( int num, int denom );
- void print( const std::ostream& os );
- Fraction operator+( const Fraction& other ) const;
- Fraction operator+( int val ) const;
- Fraction& operator+=( const Fraction& other ) const;
- Fraction& operator+=( int val ) const;
- };
- ostream& operator<<( ostream& os, const Fraction& f );
But wait! The declaration for this function is outside of our class, that is, it's a non-member function! In fact, this operator must be operated as a non-member function. The reason is relatively straightforward once you understand how the operator is called. Remember that a line like the following:
is actually called like this:
To write the << operator as a method of Fraction, the output stream would have to be the single argument to the method, and the Fraction object would have to be on the left hand side, like this:
This is obviously incorrect, since the output stream must always be on the left hand side of the insertion operator. Thus, << would have to be implemented as a member function of std::ostream. Since we cannot modify standard library classes, our only option is to implement << as a non-member function taking two arguments.
Let's look at the code for operator <<:
Code: Select all
- std::ostream& operator<<( std::ostream& os, const Fraction& f ) {
- os << f.numerator << '/' << f.denominator;
- return os;
- }
This implementation seems valid, but there's a problem. Numerator and denominator are private members of Fraction. Because of this, non-member functions such as our << operator do not have access to them. We could write accessors for the data members, however this is unnecessary and duplicates code. Looking back at our class declaration, we see that we already have a nice method to print out a Fraction to an output stream: print(). Since this method is public, let's go ahead and use it in our << operator:
Code: Select all
- std::ostream& operator<<( std::ostream& os, const Fraction& f ) {
- // Use the member function to print to the output stream
- f.print( os );
- return os;
- }
Now we have one single piece of code, contained in our class, that determines how Fractions are displayed. If we ever want to change this, we simply change it in print(), and our << operator will sill work correctly.
Style and usage guidelines
One last thing to address on the subject of operator overloading is style and usage. It can be very tempting for beginners to start overloading all sorts of operators in their classes, sometimes for purposes not intended by that operator. For example, one might be creating a List class and choose to overload the increment operator (++) to automatically add an element to the list. This can be done, and yes, it will work. But it doesn't make sense. The ++ operator is not meant for this purpose, and even though it may seem convenient, this type of functionality should be avoided.
Conclusion
You should now have a solid understanding of how operators work in C++ and how they can be defined and overloaded to allow your classes to function more like primative data types. You should understand the different between unary and binary operators, and well as when to implement operators as member functions versus non-member functions.
I always welcome questions or feedback about this tutorial. Simply post a reply or PM me; I'm glad to help!
Attachments:
(1.76 KB) Downloaded 78 times
Complete source code for the Fraction example used in this tutorial.
- Anonymous
- Bot


- Joined: 25 Feb 2008
- Posts: ?
- Loc: Ozzuland
- Status: Online
March 2nd, 2008, 12:32 am
I was browsing around today & stumbled upon an operator PHP PECL package & had little idea what good it would be, but I remembered seeing this tutorial & just now finished reading it.
Great read, I've already got thoughts along these lines
Great read, I've already got thoughts along these lines
Very cool Joebert. I've always been hopeful that operator overloading would eventually be added in PHP6, but it looks as if they have no intention of adding it anytime soon, if at all.
http://bugs.php.net/bug.php?id=9331
The PECL operator package looks promising, I might have to take that for a spin this week.
Quote:
There's an operator overloading extension, see pecl.php.net -> search for "operator" but such a feature won't go in PHP's core.
http://bugs.php.net/bug.php?id=9331
The PECL operator package looks promising, I might have to take that for a spin this week.
Page 1 of 1
To Reply to this topic you need LOGIN or REGISTER. It is free.
Post Information
- Total Posts in this topic: 3 posts
- Moderator: Moderator Team
- Users browsing this forum: No registered users and 3 guests
- You cannot post new topics in this forum
- You cannot reply to topics in this forum
- You cannot edit your posts in this forum
- You cannot delete your posts in this forum
- You cannot post attachments in this forum


