WEBVTT

00:00.120 --> 00:00.690
Hello again!

00:00.900 --> 00:05.550
In this video, we are going to add move operators to the class with RAII.

00:06.570 --> 00:11.520
So this is the string-like class that we have been building up, throughout this course.

00:11.910 --> 00:18.960
It allocates memory in the constructor, and releases memory in the destructor, which is the RAII idiom.

00:19.740 --> 00:26.310
It has two members. which are both built-in types, and we are going to add a move constructor and a

00:26.310 --> 00:29.010
move assignment operator to this class.

00:29.820 --> 00:34.590
So what do we need to do? For the move constructor,

00:34.590 --> 00:42.720
we need to move from the argument's members, into the members of the class that is being initialized. For

00:42.720 --> 00:43.530
built-in types,

00:43.770 --> 00:49.380
copying is equivalent to moving, so presumably we can just copy these arguments.

00:52.320 --> 00:56.370
Here is our String classs, with the code we have seen before, for the constructor,

00:56.700 --> 00:58.650
copy constructor, assignment operator

00:59.220 --> 01:00.420
and the destructor.

01:00.930 --> 01:01.770
In the destructor,

01:01.770 --> 01:06.270
we are going to print out the address of the pointer that gets passed to delete.

01:07.620 --> 01:14.130
There is the move constructor, in which we copy the argument members. Is that enough or do we need

01:14.190 --> 01:15.150
to do anything else?

01:19.420 --> 01:21.460
And that's not the correct thing to do!

01:21.820 --> 01:22.930
The program has crashed.

01:24.340 --> 01:29.800
If we look at the output from the destructor calls, the same address appears twice.

01:30.280 --> 01:33.400
So we are trying to delete the same pointer twice.

01:33.730 --> 01:35.050
And that is why the program crashed.

01:37.960 --> 01:44.020
The problem is with this bit of code. If you think back to the copy constructor and the problems

01:44.020 --> 01:48.870
we had with shallow copy, we ended up with two objects which have the same value for the pointer.

01:49.420 --> 01:52.720
And then when the objects get destroyed, the pointer is deleted twice.

01:53.200 --> 01:54.610
And this is really what is happening here.

01:55.240 --> 02:01.120
So let's think about what we are trying to achieve. When we move from this argument object,

02:01.480 --> 02:05.530
we are actually moving from the arguments members into these members.

02:05.920 --> 02:09.910
So after that operation, these members are going to be invalid.

02:10.450 --> 02:11.770
So let's do something about that.

02:12.400 --> 02:18.250
If we have an invalid pointer, then we can express that by saying it is equal to the null pointer.

02:21.660 --> 02:27.480
And for completeness, let's say that a size of zero represents an invalid size.

02:29.430 --> 02:32.490
We also need to make sure that the final value of this argument

02:32.490 --> 02:32.820
pointer

02:32.820 --> 02:34.320
can be deleted safely.

02:35.010 --> 02:38.340
That's not a problem because deleting the null pointer has no effect.

02:38.940 --> 02:43.920
So this should now run without crashing.

02:46.550 --> 02:46.910
Hurray!

02:46.910 --> 02:48.950
So what are we doing? In the main function,

02:49.310 --> 02:51.310
we create a couple of objects.

02:51.860 --> 02:57.920
Then we call the copy constructor and then we call the move constructor, so we can compare the effect

02:57.920 --> 02:58.880
of these two calls.

03:02.680 --> 03:09.520
In the copy constructor, we are creating a new object initialized by "b", so it has the same value for

03:09.520 --> 03:10.090
the size.

03:10.930 --> 03:17.770
The address pointer is different, because this has its own memory allocation. With the move constructor,

03:17.780 --> 03:28.000
on the other hand, the new object ends up with the same values that "b" originally had, and "b" is now invalidated.

03:28.780 --> 03:36.810
And you can see there that the null pointer is deleted, without any ill effects. For the move assignment

03:36.820 --> 03:38.920
operator, we have two options.

03:39.490 --> 03:44.710
We can write something which is similar to the traditional copy assignment operator. So we check for

03:44.710 --> 03:52.990
self-assignment, then we delete the original memory allocation, and then we get the memory.

03:53.200 --> 03:56.470
In this case, we do not allocate a new memory allocation.

03:57.160 --> 04:03.910
We steal the memory allocation from the argument object. And then we need to invalidate the argument

04:03.910 --> 04:04.360
object.

04:04.840 --> 04:06.190
So this is really the usual thing.

04:06.190 --> 04:09.160
We have the destructor code followed by the constructor code.

04:09.490 --> 04:13.570
In this case, it is the move constructor code and not the copy constructor code.

04:20.060 --> 04:22.100
So there is our move assignment

04:22.100 --> 04:26.840
operator. And again, we return by lvalue reference.

04:29.180 --> 04:34.790
In the main function, we create a couple of objects and then we call the copy assignment operator and

04:34.790 --> 04:36.440
the move assignment operator.

04:42.390 --> 04:50.460
So with copy assignment, the object which is assigned has the same size member.

04:50.790 --> 04:57.180
It has a different address because, again, it allocates its own memory allocation. With the move assignment,

04:57.600 --> 04:59.940
so we are move assigning from this object.

05:02.060 --> 05:08.480
The assigned-to object ends up with the same data members that the original object had, that we are assigning

05:08.480 --> 05:12.080
from, and the assigned-from object is now invalidated.

05:14.420 --> 05:20.090
The alternative is to use something similar to copy-and-swap, which is easier to write but a bit harder

05:20.090 --> 05:20.780
to understand.

05:21.860 --> 05:23.750
So we are actually going to use move-and-swap.

05:24.200 --> 05:25.550
But the basic idea is the same.

05:25.940 --> 05:29.450
We create a temporary object which is initialized from the argument.

05:30.320 --> 05:36.770
Then we swap the members of this temporary object and the object we are assigning to, and then we return

05:36.770 --> 05:37.730
the assigned-to object.

05:38.480 --> 05:45.110
When the destructor is called, this temporary object is destroyed, and that will release the memory

05:45.110 --> 05:48.260
allocation from the original assigned-to object.

05:52.240 --> 05:58.240
We are using the move constructor here to initialize the temporary object, so we need to cast the argument

05:58.570 --> 05:59.410
to an rvalue.

06:00.580 --> 06:06.520
This will call the move constructor, which will set the argument's pointer to null, and the temporary object

06:06.520 --> 06:09.130
will have the original pointer from the argument.

06:09.820 --> 06:10.930
Then we swap them around.

06:11.800 --> 06:18.040
The temporary object now has the original pointer from the "this" object, that we are assigning to and "this"

06:18.040 --> 06:20.080
now has the pointer from the argument.

06:21.220 --> 06:26.800
And then, when we return, the destructor will release the original pointer from the "this" object.

06:31.740 --> 06:37.440
So there is our move-and-swap operator, and we have the same main() function again.

06:40.900 --> 06:43.770
So the output is a bit different for the move assignment

06:43.780 --> 06:44.320
operator.

06:44.920 --> 06:50.260
We have the move constructor call, for this initialization of the temporary object, and then we have

06:50.260 --> 06:53.470
the destructor call, when the temporary object is destroyed.

06:54.550 --> 07:01.630
So the address that is released is this one, which was the original pointer in the object that is being

07:01.630 --> 07:02.950
assigned to, this object

07:02.980 --> 07:03.430
"c".

07:04.550 --> 07:13.460
So the object "c" ends up with the data from the original object "a", the members, and "a" is invalidated.

07:16.100 --> 07:17.660
Okay, so that is it for this video.

07:18.050 --> 07:18.920
I will see you next time.

07:18.920 --> 07:21.320
But until then, keep coding!
