Operator Overloading
Operator overloading is very similar to function overloading. The thing to remember is that when an operator is used with a class, a function call is actually created. Operators are just a shorthand notation for that function call.
When you create a class it is possible to give an operator a new meaning when used with that class. For example, we have used the vector class provided by the STL. There are two ways to access an element in a vector. One way is to use the .at() method:
| vector <int>
myVector;
myVector.at(5); |
The other method is to use the [ ] operator and access an element as if the vector were an array:
|
vector <int> myVector;
myVector[5]; |
Both methods accomplish the same thing, but the second method is much more intuitive because we are already used to dealing with arrays. The reason we can make the vector class act like an array is because the programmers that created the vector class were nice enough to overload the [ ] operator.
Operators Made "Simple"
Let's look at a simple example. We will create a class called Point that has members x and y:
|
class Point { public: Point(int newX, int newY):x(newX), y(newY){ } //constructor Point operator +(Point); //overloaded + operator, treat it just like a member function int GetX(){
return x;} //accessor |
First, note that the + operator is treated very much like any other member function in the class. It has a Point return type and takes in a Point as a parameter. The only real difference is the function name. The function name is just operator and then what the operator is, + in this case.
The implementation is a little strange so let's break it down line by line.
|
//implementation of + operator Point Point::operator +(Point p) { int newX = p.x + x; int newY = p.y + y; return Point(newX, newY); } |
The first line looks very much like a standard member function implementation. We see a Point return type, we see that the function is a member of the Point class by the :: scope operator. Again we see the operator keyword followed by the + operator. Finally, like any other function, we have a parameter list. This function takes in a Point which I have called p.
Inside the overloaded operator lurks some more strange stuff. I created two temporary variables, newX and newY. The value of member x of the Point that was passed in (p.x) is added to the value of x that is a member of the instance of the class that the + operator belongs to. The same is done for newY to hold the sum of the y values. The two temporary values are then used to create a new Point which is returned. I have written this out in three lines for readability although it could be reduced to just one line. The one line version looks like this:
|
Point Point::operator +(Point p) { return Point(p.x + x, p.y + y); } |
The important thing to think about is why are we overloading the + operator for this class? In this case we want an easy way to add the x and y members of Point objects together. It may seem like a complicated way to do things, but remember, overloading operators is suppose to make life easier for the user of the class, not for the developer of the class. You have to consider how a user might want to use your class and you have to provide ways to let the user easily do those things. For example, it is perfectly reasonable to expect the user to try something like this:
|
Point apoint(10,4); Point anotherpoint(6,10); Point addpoints = apoint + anotherpoint; |
When the + operator is used on "apoint" and "anotherpoint", their x and y members are added and a Point object is returned. The Point object that is returned is the one that gets assigned to "addpoints". If no overloaded + operator were provided the user of the class would have to do something like this:
|
Point apoint(10,4); Point anotherpoint(6,10); Point addpoints(apoint.GetX() + anotherpoint.GetX(), apoint.GetY() + anotherpoint.GetY()); |
Although the end result is the same, the overloaded operator way is much simpler and more intuitive.
Streamin' Along
Let's look at a trickier operator to overload, the << operator. Overloading << is very similar to overloading +, but there are a few extra steps.
|
class Point { friend ostream&
operator <<(ostream&, Point); int GetX(){
return x;} //accessor |
Notice that the << stream operator is not a member of the class, it is a friend function. The operator returns the address of an ostream object and takes in an ostream object by reference. It also takes in a Point object. Here is the implementation:
|
ostream& operator <<(ostream& out, Point p) { out << "[" << p.GetX() << ", " << p.GetY() << "]"; return out; } |
The operator takes in an ostream (out) and a Point object (p). "out" is used to print the x and y members of "p" to the screen. "out" is then returned so that more text could be added to it, if the user desired. Now the user can do something like this:
|
Point apoint(10,4); Point anotherpoint(6,10); Point addpoints = apoint + anotherpoint; cout << "apoint = " << apoint << endl; cout << "anotherpoint = " << anotherpoint << endl; cout << "addpoints = " << addpoints << endl; |
First, two Point objects are created and given some default values. Then, the two Point objects' x and y members are added together via the + operator and the result is assigned to a new Point object, "addpoints".
Now, we get to the newly overloaded << operator. The output from the 4th line down is:
apoint = [10, 4]
anotherpoint = [6, 10]
addpoints = [16, 14]
Look closely at what is going on:
| cout << "apoint = " << apoint << endl; |
| ostream& operator <<(ostream& out, Point p) |
"cout" is passed to the overloaded << operator by reference and becomes "ostream out". "apoint" is passed in as "Point p". Because "out" is a reference to "cout", streaming data into "out" really streams the data into "cout".
Almost every class you create should provide the overloaded << operator. Just get into the habit.
Deep Thoughts
Most operators can be overloaded, but there are a few that cannot. The ::, sizeof, ?:, and . (dot) operators cannot be overloaded. It is also not possible to create new operators. Keep in mind also that the operators +, +=, ++, -, -=, and -- are each separate operators, therefore overloading + does not automatically overload +=, etc.
Operator overloading is powerful, and overloaded operators can be made to behave in strange ways. For example, you can make the + operator perform subtraction or the * operator perform division, DO NOT DO THIS! Again, it is up to you, the developer of the class, to think about how the user would expect an operator to behave. Take the time to make sure your operator implementation is going to "play nice".