C++ classes may overload operator functions to customize the behavior of operators when used with objects.
- [Narrator] Operator overloading is not unique to C++, in fact it was one of the original concepts borrowed from ALGOL for C++. But how C++ does it is fairly unique, and extremely powerful. There's two distinct ways to overload operators in C++. One is with member functions as part of a class definition. The other is with separate non-member functions. In this movie we'll be discussing the overloading operators with member functions in your class definitions, and we'll discuss the non-member functions in the next lesson.
So here I have a working copy of rational.cpp from chapter one of the exercise files, and you'll notice that we have this rational class which is simply a demonstration class for doing four function arithmetic on rational numbers, that is numbers that have a numerator and a denominator like a fraction. And so we've got the numerator and denominator private data values, we have a couple of constructors, one of them constructs with two integers, a numerator and the denominator.
And you notice that it has default values for those, so this'll also operate as a default constructor, because it has default values for both of those arguments. And then we have a copy constructor, and this simply copies over the values from another rational object. And we have a destructor, a couple of getters for the numerator and denominator, and then we have our operators. So the syntax for an operator overload is first the leam of the class, and then the keyword operator that says this is an operator overload, then the operator itself and then the arguments.
In this case, for each of these, because they are member functions, they only need one operand, the left hand side of the operation is the object itself, and the right hand side is the argument, or the operand. All of these are const safe, because they return a separate new object. They're not returning a reference to the existing object. So if we come all the way down here to our main, you can see all of this in action. Here's our various constructors who are constructing an object here with just a numerator, so it's seven over one.
Here we're constructing an object with five thirds, so it's got a numerator and a denominator. Here we're using a copy constructor, a default constructor. Then we use an assignment operator on the object that we had just default constructed. And this one is just a reference, so no object is created. And here's an assignment to self, we'll talk about that in a minute. And then here's our four examples of arithmetic, we've got addition, subtraction, multiplication, and division.
So I'm going to build and run here. And we scroll up, we see all of our different constructed objects, and here's the results of our arithmetic. So let's take a look at how all of this works. Here's our four function arithmetic operators, they all work exactly the same. They do a little math to create the numerator, and the denominator, and it's just very simple. We're not reducing the lowest common denominator or anything like that.
This is all just very simple, and it uses these two results to construct a new rational object, and it returns that rational object as the result. And all four of these work in exactly the same way. They take a right hand side, the object itself is the left hand side, they do the necessary math, pass the new numerator and denominator to a new rational object to instruct a new rational object and return that object. And in each of these use cases, because that resulting rational object is not assigned to anything, it's just temporary.
It's used for c:out, and then it's discarded. And then I want to talk about the special case of the assignment operator, or the copy operator as it is sometimes called. So this is the assignment operator, and you'll notice that it uses the 'this' pointer in a couple of ways, one is that it returns a reference to itself. You need to be able to chain assignments, you need to be able to say x equals y equals z, and have all of that work. In order to do that, the assignment operator returns a reference to the object itself, and that's accomplished by using the 'this' pointer.
What's pointed out by this, asterisk this, is the object itself, and we're returning a reference by virtue of the reference operator right there. The other use is to test if we're assigning to ourself, if we're copying from the same object, in which case we don't really need to do anything. All we need to do is return the reference. And so this is accomplished by testing this, which is a pointer, to this object, the one that we're operating in, and comparing that to the address of the object to the right hand side.
And if they're not equal we go ahead and do the assignment, and if they are equal we skip all of that and just return the reference. So there's one other operator overload that I want to talk about and this is specifically for c:out, which is the ostream, it's the out stream. And so we're overloading the left shift operator, which is how c:out works. With an ostream on the left, and a rational object on the right.
And this is outside of the class definitions, so this is a non-member function operator. And I just wanted to mention, and we'll go over the details of this at another time, but I just wanted to mention that this is an important technique. This makes c:out work. If I comment this out and try and build, you'll notice that all of our c:outs no longer compile, because they don't know what to do with a rational object. So if you want your object to work with c:out, you need to have that operator overload, and that's how that's done.
We'll talk more about nonmember operator overloads in the next movie. Operator overloading is a fundamental part of C++, it's as easy to do as defining a class method. And we'll look at some reasons to use nonmember functions for operator overloading in the next lesson.
- Classes and objects
- Constructors and conversion operators
- Class inheritance
- Smart pointers
- Move semantics
- Lambda syntax
- The C preprocessor
- Unit tests
- Building a custom string library