Welcome back, so we've learned how to overload the copy and
move assignment operators.
Now let's look at some of the other operators that c++ lets us overload.
C++ allows us to overload operators as member methods or
as global non-member functions.
In this video we'll use member methods to overload operators.
And in the next video, we'll overload the same operators
using global functions.
So let's start with unary operators.
Unary operators work on one operand.
Those operators include the increment, decrement, unary negation
and the bang or not operator.
Notice the basic method declaration in bold.
In the case that we have to return a new object from the method, we'll
return the new object by value.
Also notice that unary member methods have an empty parameter list.
This is because the object we're working with is referred
to by the this pointer.
So here you can see the prototypes for the unary operators.
If we wanted them to work with our hypothetical number class.
Also notice that we can overload both the pre and post decrement
and increment operators.
C++ simply provides an int that isn't used in the parameters
for the post increment.
At the bottom of the slide, you can see how we can use the unary
operators as we do with the built-in types and what methods are being
called in the comment section.
Suppose we wanted to overload the unary minus operator to
work with our my string class, and we want to make the string
lower case whenever it's used.
So we can create a my string object named Larry1 with the
initializer string uppercase Larry.
Then we can use the unary minus operator on Larry1 and
store the result in Larry2.
Now Larry1 will still contain the uppercase Larry string,
but Larry2 will contain the lowercase Larry string.
This is a simple example of unary operator overloading using member
methods, however, the question is whether this makes sense.
We're programmers we can make anything happen in code.
Does it make sense for the unary minus operator to mean
make the string lower case?
It depends on the context of the application and the design.
Personally, I'd prefer a method named lowercase or lowercase or too
lower since that's very explicit.
But this might make sense in another design.
Let's see how the code might look.
Here's the code for overloading the unary minus
to make a string lower case.
Notice the method header.
We're returning a new object that will be the lowercase
copy of the existing object.
We're overloading the operator minus.
We expect no parameters to this method.
And it's a const method since we don't want to modify the current object.
We want to create a new object based on it.
So the first thing we do is allocate space for an area in
memory on the heap where we want to store the lowercase copy
of the current object string.
Then we use the stood string copy function to copy the string over.
Then we simply loop through the characters in the copy and
convert each character to its lowercase equivalent, using
the character function to lower in the cc type header file.
Once we have the buffer lower case, then we construct a temporary
mystring object using this lowercase string as the initializer.
Finally, we delete the buffer we created in this method so we don't
leak memory and we return the newly created lowercase object.
We could certainly write more optimized code here, but I wrote this
method to show each step explicitly.
So that's the general pattern for overloading a unary
operator using a member method.
Now let's see how we can implement some binary operators
also using member methods.
As we know binary operators are applied to two operands.
These operators include addition, multiplication, division the
equality operators and many more.
The pattern for overloading these operators is very similar to the way
we overloaded unary operators except that now we have a single parameter
in the method parameter list.
I've named this parameter RHS, which stands for right-hand side.
That way it's very clear that RHS refers to the parameter on the
right-hand side of the operator.
And the this pointer points to the object on the left-hand
side of the operator.
Notice the overloaded addition and subtraction operators
return the newly created sum or difference objects by value.
Also notice that the equality operators return Boolean
values which makes sense since they're comparing things.
You can see in the bottom of the slide how these operators might be used
in our hypothetical number class.
Now let's see how we can implement the equality
operator in the mystring class.
In the case of my string objects, we want to compare the strings
to see if they're the same.
We don't want to compare the pointers.
Instead, we want to compare the strings that the pointers point to.
Rather than write all the code ourselves, we can use the stood
string compare function in the c string header file, which compares
c-style strings exactly as we need.
When we call that function with two c-style strings, it will
return 0 if the strings are equal.
So this method becomes almost trivial to implement.
We compare str from this object to scr from the right-hand side object.
And if the result is 0, we return true, otherwise we return false.
That's it.
Now we can write code like if s1 equals s2 to compare two
mystring objects for equality.
What if we wanted to write an overloaded equality
operator for the player class.
Well, we'd have to decide what we want to compare.
Are we comparing the names of two objects or the health
or the xp or everything.
This would be an important decision and the programmers using the
player class would have to be clear as to what's being compared
so they can use it correctly.
Now let's wrap up these slides by overloading the addition
operator for the mystring class.
Suppose we wanted to overload the binary plus operator for
the mystring class so that it concatenates the strings from
the left-hand side and right hand-side objects into a new object.
Then we could use this as in the example code.
First, we can create my string objects Larry and Moe and Stooges.
Then we can write a statement like Larry plus Stooges which
will concatenate the string Larry.
And the string and stooges to create a new string.
Larry is one of the three stooges.
Notice that we can also use c style strings on the right-hand side.
This is because we have a mystring constructor that can
construct mystring objects from a c-style string.
That's pretty cool.
The only limitation to overloading operators as member functions
is that the object on the left-hand side must be an object
of the class you're using.
For example, the last statement in this slide won't compile
since the left-hand side operand is not a my string object.
We can get this to work in the next video when we implement
operators as non-member functions.
Okay, so let's take a look at the code for this method.
It's probably exactly as you would expect.
We allocate a character buffer large enough for both of the strings
plus one for the string terminator.
Then we allocate this buffer on the heap.
First, we copy over the left side string with std string copy, and
then we concatenate the right side string with std string cat.
We create the new object that will contain the concatenated string
using the buffer as the initializer.
And then finally, we de-allocate the local buffer
area so we don't leak memory.
And then, we return the newly created object by value.
You can see that first this looks complicated.
But the more you do it patterns evolve that are very very similar.
Great, so let's head over to the IDE and implement these operators for the
my string class as member methods.
Okay, so I'm back, and I'm in the IDE.
I'm in the section 14 workspace in the mystream-operator-methods project.
In this project, what we're going to do is we're going to overload
three operators as member methods.
And the ones we're going to overload are right here the equality
operator that allows us to compare two objects as we can see here.
The negation operator, the unary minus operator, which in our
case we're going to make whatever string we want to negate lowercase.
And finally, the concatenation operator that'll take two string
objects and make a bigger string object from it that contains
those two strings put together, just like we've done before
using the regular string class.
And you can see we can extend this so we can have Moe plus blank plus
Larry plus blank plus Curly, and this is a c-style string right here.
So it'll be pretty flexible.
Okay, so why don't we start.
We're going to implement them as member methods.
So let's go to our header file, and create our method prototypes here.
In the case of the concatenation and the unary minus where we're going
to make things lower case, we're going to return mystring objects.
So we'll create a mystring object, and it's going to be returned by value we
want to create a new object, right.
We're going to take those two strings, make a new one, return it.
We're going to take that one string, make it lower case
into a new one, return it.
We're not going to change anything that we've got going on already.
So let's do the operator minus first okay.
So it's going to be the operator.
We're overloading, again, is operator followed by the operator we
want, which is minus in this case.
And what are we going to be passing into this, nothing.
That's a unary operator.
So we've already got what we need into the left side of the dot.
It's important to understand that when we're overloading operators
as member methods unary operators take no parameters, binary operators
take one parameter because this is already a parameter on the left side.
And we want this to be const.
And this will mean, let's say, make lowercase version of the string.
All right, now let's do the concatenation operator.
This is a binary operator.
So we need one parameter in this parameter list, and that parameter
will be const mystring reference, and we'll call it right-hand side.
Important that it's const because we really don't want to mess with that.
And then the last one is the equality operator.
It will return a Boolean, a true false value.
And it will be operator, two equal signs.
It's binary.
So it's going to expect a const mystring reference, right-hand
side and we want this to be a const method as well.
So I don't want to change anything.
Actually, I want this one to be const as well.
That's it.
So those are the three prototypes.
Now what we'll do is we'll go to the mystring cpp file,
our implementation file, and we'll implement those methods.
So let's do that now.
Okay, so I'm going to put them right here right after the
overloaded assignment operators.
And why don't we start with the overloaded equality operator.
So that one is, again, bool.
It returns a Boolean.
It's part of mystring class, the name is operator, two equal signs.
And it expects on the right side a mystring reference to a constant.
We'll call it RHS, and the method is a const method as well.
Okay, so what do we do here?
Well, we want to compare those two strings, right.
The string in this and the string and right-hand side.
So all we really need to do is just simply say return, and then we can
call std string compare, which is a c-string function that compares
two c-strings is which we've got.
So str is one of them.
And right-hand side dot str is the other one.
And if that returns 0, we'll return the value there.
Okay, so simple as that.
We could use an if else statement if we like.
But this works just as well.
So that takes care of that, and this will be, let's see, equality.
Now let's do make lowercase.
And in this case, we're returning mystring object by value.
It's part of the mystring class.
There's my scope resolution operator, and it's operator minus
nothing in the parameter list because it's a unary operator.
And it's const because we don't want to change our current object again.
Now what do we have to do here.
Remember, we need to make a copy of whatever is in the current
object, make that lowercase, and then create a new object from it.
I need to allocate space to create that copy so
that I can make lowercase.
I don't want to change the object because I can't right.
I don't want to. It's a const method.
So what do we do.
Well, let's create a temporary area and memory called
buff, and we'll create a new array of characters there.
And how big is that going to be.
Well, it's going to be as big as the length of the string in str plus 1.
And again, we're using std string length here, given that string.
So if it's frank, I'm going to get back 5, allocate 1.
This is a brand new pointer to an area on the heap that I just allocated.
It's local to this function.
So when we're finished with function, we need to get rid
of that and de-allocate it.
So now what do I do.
Well, at this point I need to copy the string that's in str to buff.
Okay, now we've got a copy of that string.
So what we can do now is we can just simply loop through
that, and set I equal to 0.
And we can go while I is less than, let's say, the string length of str
or the string length of buff actually.
So they're both the same right now.
And we're going to go by one.
That's it. And now inside the loop, what we want to do is we just simply
want to say buff sub I equals the lowercase version of it, right.
So std, and we're going to do to lower which is a function
in the cc type header file.
So we'll say to lower and to lower what, well, the
character itself buffs by.
That's it. Now we've got what we need.
Buff now contains a copy of that original string and then
we just made it lowercase.
So what do we want to do now.
I want to create a new object, the temporary object right on the stack
here based on that information.
So I'm going to say my string.
We'll just call it temp, and we'll initialize it from buff.
Now we want to return temp, and then we'll be done.
But there's one thing we need to do and it's really easy to forget and
it's -- this is one of the benefits of using smart pointers, which we'll
talk about later on in the course.
Right here I allocated this area in memory.
I've used it right here.
I'm done with it.
I need to de-allocate that space.
Otherwise I'll leak that memory.
So I want to be sure that right here I delete buff.
And that's it.
That takes care of that.
And then the last method is the concatenate method, which,
again, is really straightforward.
We're going to create a new string from it.
So by value, it's a mystring method.
We'll call it operator plus.
And it expects something on the right-hand side, which
is mystring
by reference. We'll call it RHS, as usual.
And it's a const method as well.
That's it. So what do we do here.
Well, same idea here, right.
I just need to allocate an area in memory for characters.
We'll call it buff again.
And how big is this going to be.
Well, this needs to be since we're concatenating, it has to be the length
of this string plus the length of the right-hand side string plus 1.
So that's all we're going to do.
We're just going to say new char, how many.
Well, the length of str plus the length of right-hand
side's str plus 1.
That allocates the storage on the heap that I need.
At this point, I need to copy the first string and then concatenate
the second string to it so I can build the bigger string from it.
So we'll say std string copy into buff from the left-hand
side string which is str.
Then we'll do a std string cat to concatenate again into buff
from the right-hand sides str.
Perfect. So what we've done now is we've just copied one string
and concatenated the other.
At this point, we've got the information we need in buff
to create that new temporary object that I'm going to return.
Same thing we did up here.
So we'll build that object.
We'll call it temp, and we'll build it with the buff information.
Remember, delete buff.
We're done with it now.
And return that temporary object.
So that's it. We should be done unless there's any typo.
So let's compile and run.
And you can see here we've got a copy constructor being used.
Larry is 5, Moe is 3.
We got back false and true, exactly what we expected.
We get Larry uppercase, Larry lowercase l.
Then we have Moe and Larry, Moe, Larry and Curly.
So everything looks great.
All right, so there you go.
That's an example of implementing these three overloaded
operators as member methods.
You'll get a lot more chance to practice this when we get
into the challenge exercise.
What we'll do in the next video is we'll do the same three operators, but
we'll do them as non-member functions.