In this video, we'll overload the move assignment operator
for our mystring class.
Remember, the copy assignment operator that we just overloaded
in the previous video works with l-value references.
The move operator that will overload in this video works
with r-value references.
Again, think temporary unnamed objects.
You don't have to provide a move assignment operator.
And if you don't, c++ will use the copy constructor by default.
But as we said when we talked about the move constructor, move
semantics can be much more efficient, and it's not a lot of extra work
to provide a move constructor and a move assignment operator.
First, let's see when the move assignment operator will be used.
In the code snippet in this slide, you can see that we create
a mystring object s1 that's basically just an empty string.
Now we assign the mystring object with the string frank to it.
Notice that the mystring object initialized to
frank must be constructed.
And once it's constructed, it has no name, it's a temporary object.
So it's an r-value.
That means that when we assign it to s1, the move assignment
operator will be called since we're providing an r-value reference.
Let's see how to do this syntactically.
You'll notice the move assignment operator declaration is very
similar to copy assignment.
But there are a few differences.
First, we use the double ampersand operator in the parameter list
to tell the compiler that the right-side object is an r-value.
So the right-side value will be an r-value reference.
Second the right-hand side object reference can't be const
since we'll be modifying that object when we move the data.
So the declaration for the mystring class returns a mystring object by
reference and expects an r-value reference to a mystring object.
You can see that in the two assignment statements on this slide, the move
assignment operator will be called because the right-hand side of the
assignment statements are r-values.
So let's look at the code for the move assignment operator for this example.
As you would expect, the code is very similar to the copy
assignment with the exception that we're not deep copying
from the right-hand side object.
Instead, we're simply stealing the pointer and then nulling
out the right-hand side pointer.
Again, you can see from this code how much more efficient this
is compared to copy assignment.
Let's walk through this one step at a time.
First, we check for self-assignment just before.
And if we're self-assigning, then we return the left-hand
side object, and we're done since there was nothing to do.
Otherwise, we again de-allocate the storage pointed to by the left side
object since we'll be overriding this, and we don't want a memory leak.
Now the left-hand side object is ready.
So let's move the pointer over.
First, we steal the pointer from the right-side object and assign
it to the left-side object.
This is simply a copy of a pointer variable.
We're not doing a deep copy here.
Then we know out the pointer on the right-side object.
This step is critical.
Finally, we return the left-side object.
Now let's head over to the IDE and compare the move
assignment operator to the copy assignment operator in live code.
Okay, so I'm back in the IDE.
I'm in the section 14 workspace in the mystring-move-assignment project.
Now what I'd like to do in this project is add a move assignment
operator to our mystring class.
This can be really useful when we're assigning r-value references.
So let me give you an example of how this would work.
Here we've created an object a, and it's using an overloaded
constructor that's expecting that character pointer.
And we're just constructing a simple object a that's
got that string attribute pointing to hello on the heap.
Now at this point, we want to use assignment.
Remember, a already exists so now this will be assignment not initialization.
And what I want to do is construct another mystring object off of
the initialization string hola.
At this point, that temporary object has no name, so it's an r-value.
If we don't provide any move semantics, then it's going to use a
copy constructor or a copy assignment operator to do what it needs to do.
And if I run this right now, remember, I don't have any move constructors
-- sorry, I don't have any move assignment operator implemented.
So if I run this at this point, you'll see that it's using copy
assignment, using copy assignment.
Now we really could use move assignment here since we've
got those r-value references, and that's what we'll do.
So let's go over conceptually what's going on here again.
At this point, we've created that a object, and it's on
the stack, it's got the str attribute, which is a pointer.
And once this object has been constructed that pointer
is pointing to the string hello, which is on the heap.
And again remember, it's got that null terminator at the end.
So this is the situation once we instantiate a.
Now what we want to do is we want to assign to a the object
that we created right here.
So let's create that object.
That object gets created exactly the same way as we created a, right?
It's using the overloaded constructor.
But it doesn't have a name.
It's an unnamed temporary object.
So I'm just going to leave it right over here like this without a name.
It's got an str attribute, and it's pointing to hola,
which is on the heap.
So that's the situation like right now.
Now what we want to do is we want to assign this guy over to this guy.
But we really don't want to do the overhead of copying
because we don't need to.
This temporary object will be destroyed soon.
So let's just use move assignment so that we can steal that pointer
and not have to make any copies of anything or copy anything over.
Okay, so let's do that.
How does this work, what are the semantics.
Well, the first thing we need to do is de-allocate the space here, right.
We need to call delete for this guy because we're not going to use that.
That's the whole point.
I want to assign to it.
So we're going to get rid of that.
And what that's going to do it's going to leave us in this situation where
we're no longer pointing to that area.
And then what we want to do is we want to steal the pointer.
So I want to make this pointer here point to hola.
And then once that's done, what I want to do is not delete this.
I don't want to delete this information here, right.
I don't want to de-allocate that because I'm using it now over here.
All I want to do is just null out that pointer.
So that's what I'll do.
I'll just null this pointer out.
So this guy is now null.
You'll notice a is now though the string attribute a is now pointing
to the correct string in the on the heap which is what I want.
This guy has been nulled out.
So when it gets destroyed by the destructor, it won't delete anything
out there on the heap because it's not pointing to anything on the heap.
So once this gets destroyed, we're right back to here, which
is exactly where we want to be.
And the same is true with this statement right here.
A is bonjour.
We'll create a temporary object.
There's the str, and it's going to be pointing to bonjour, right.
Now when we assign over, we'll do the same thing.
We want to de-allocate that storage right here.
So we want to get rid of this.
We want to move the pointer, right.
We just want to seal this pointer let it point over here.
And then what we want to do is set this pointer
right here to null, right.
So we just move the data simply by just pointing to
it from a different place.
This will be destroyed.
And that's the effect that we want.
Okay, perfect.
So now let's write some code for this.
And you'll see it's actually a little bit simpler even
than the copy assignment.
First things first.
Let's go to the header file.
And you'll notice that I've already implemented a move constructor.
I just wanted to be complete there, so the move constructor
has been implemented.
And it's about as dead simple as you can get.
Let me show you what the move constructor looks like.
It's right here.
Remember, this is a constructor now.
This is not an assignment, so this is actually creating a new object
based on an r-value reference.
So you can see right here on line 35, I'm stealing the pointer, right.
So I'm taking the source object's pointer and moving it right to str.
And then I'm nullifying the source object pointer.
So that's it.
We just stole the pointer, that other object will eventually be destroyed.
Let's write the move assignment operator.
So let's go back to our header file, and we'll do it right here.
And we'll do something like mystring reference because
it's the same idea here.
We're overloading operator equal.
We don't want to be const here because we do need to modify
that right-hand side's string attribute to null it out.
So it's simply going to be mystring, two ampersands here because we wanted
our-value reference right-hand side.
That's it.
And we'll call this guy move assignment.
All right, so now let's go implement that method.
So we'll go to mystring cpp file.
And right underneath the copy assignment right here, I'll just put
it right here under move assignment.
And you can see I've already set some break points just
to save a little time.
So let's implement this here.
So what do we do.
We're going to return a mystring reference, and it's
part of the mystring class.
We're overloading operator equal, and we're expecting a
mystring r-value reference.
Okay, that's it.
So first thing we do is as usual let's just put a string out on std
cout, and we'll just say something like using move assignment.
That way we know that we're actually in here.
And we'll put an endline here.
Perfect. Now let's check for self-assignment.
I'll scroll up just a little bit here.
We'll check for self-assignment so we're going to say if the current
object, right, the address of this object is equal to the address of
the right-hand side object, if that's the case, I've got self-assignment.
So all I want to do is just return this.
In the case that we're not doing self-assignment, remember, the
conceptual drawing I just drew.
So what do we want to do.
We want to delete str from the current object.
We want to steal the pointer.
So we want to assign the right-hand side's pointer to
the left-hand side's pointer.
And we want to nullify the right-side pointer.
This one's really, really important.
If you forget to do this step, then there's going to be problems because
what's going to happen is when that temporary object gets destroyed,
it's going to take this data with it.
So you want to be sure that this is pointing to nowhere.
So right-hand side.string equals null pointer.
That's it.
We return this.
Now we'll step through this in a second, but let's run it first.
And now you can see the difference in the run, using move
assignment, using move assignment.
That's exactly what we want.
And then we've got two destructors being called for null pointers, right,
which is again exactly what we want.
All right, so let me put a break point here.
And let's walk through this using the debugger.
So I'm going to go back to my main here.
And I'll start the debugger over here.
All right, so this is where we're at.
Right now, we're about to call the overloaded
constructor to create the a.
That's pretty standard stuff now.
Here we are.
We don't have a null pointer.
So we're simply going to allocate space for the string,
copy it over, and we're done.
So now a is hello, and you can see the address of that
string attribute right here.
Now what we need to do here is first construct that temporary
object and then call the overloaded assignment operator, right, with
an r-value reference because that's exactly what this is.
This is an unnamed object so let's walk through this.
So first, we're going to create the object using the overloaded
constructor right here.
So we'll walk through that, allocate the space, copy it over.
It's done.
Now notice that the move assignment operator gets called right away.
That's exactly what you expect because we have an r-value reference here
which is a temporary unnamed object.
So we display using move assignment, we check for self assignment, which we
don't have, we delete this str, right.
Remember, this is this object right here.
So we're going to delete that and de-allocate it.
We're going to steal the pointer, and then we're going
to nullify that other pointer.
Now the temporary object is being destructed right here.
And we do have a null pointer now because we nulled it out.
You can see it's 0 right there.
So this will print out calling destructor for null pointer.
We'll delete the area.
And that's it.
Now if we refresh, you'll see that a now is hola.
So we did exactly what we said we were going to do.
Here it's the same idea.
First, we're going to construct a temporary object based on that string.
So let's do that.
Here we are.
We're in the overloaded constructor and that's going to create a
temporary object with bonjour in it.
Then we call the move assignment again because we have an r-value
reference and what we're going to do here is we're just going
to steal that bonjour pointer.
First, we delete what we're pointing to, right.
So hola is gone.
Then we steal the bonjour pointer, and we set that temporary
object's pointer to null.
We return the object, and then we're going to destroy that
temporary object again the one that we just stole the pointer from.
We'll delete it.
We're back, and you can see now that a is bonjour.
We'll unwind now, and we'll destroy a.
There you go.
And you can see over here that we've got two move assignments,
three destructors; two destroying just null pointers temp objects and
then the final one destroying the actual a object that we created.
That's it.
So you can see it's pretty straightforward.
And we'll see that in the main driver here, I've got a lot more code.
And if you recall that code running from before, so I'll
run this code again in the main now that I've uncommented it.
And if you remember, when we ran it before, it was using copy
constructors and copy assignment.
Well in this case when we run it, we're going to see a lot
more move semantics going on.
So I'll scroll up here, and you can see that we've got move assignment,
move assignment, move assignments, move assignments, move constructors.
So this version is much, much more efficient.
And if -- again, walk through it, check out the the statements, use
the debugger if you like and really understand what's going on, but
it's very very straightforward.
In the case of r-values, we really want those move assignments and
move constructors just to make our code so much more efficient.