WEBVTT

00:00:00.540 --> 00:00:04.010
Dynamic arrays let us specify the size at runtime.

00:00:04.019 --> 00:00:08.460
But what if we don't know how much elements are we going to need in the future?

00:00:08.840 --> 00:00:13.030
What if the requirements fluctuate from 0 to 500 elements

00:00:13.040 --> 00:00:15.970
based on the usage of the program in question?

00:00:15.980 --> 00:00:19.540
Well, of course, we could create an empty vector with 0

00:00:19.540 --> 00:00:23.400
elements and then resize it to fit one more element every

00:00:23.400 --> 00:00:25.850
time we have a need for a new subject.

00:00:26.240 --> 00:00:30.210
But this would first create a new empty subject every time we

00:00:30.210 --> 00:00:34.140
resize the vector. I don't want to do that because I already know

00:00:34.140 --> 00:00:36.660
that I will initialize these objects later.

00:00:37.040 --> 00:00:40.650
Fortunately, vector works kind of like a stack data structure.

00:00:40.650 --> 00:00:45.160
So if you want to add a new element, you can just use the push_back function.

00:00:45.940 --> 00:00:49.820
This will first resize the vector to fit one more object, and then it

00:00:49.820 --> 00:00:53.600
will construct this new object by using the move constructor, which

00:00:53.600 --> 00:00:56.350
will move data from this temporary subject.

00:00:57.440 --> 00:00:59.560
Let's compile this to see it in action.

00:01:00.840 --> 00:01:03.440
So the vector will no longer use the default

00:01:03.440 --> 00:01:06.050
constructor to create an empty object.

00:01:06.060 --> 00:01:08.440
This new object will be pushed to the back of the

00:01:08.440 --> 00:01:11.360
array immediately after the resizing.

00:01:12.240 --> 00:01:12.840
However,

00:01:12.840 --> 00:01:16.500
you can see that after each push, the number of move constructor

00:01:16.500 --> 00:01:21.690
invocations increases. This would make sense because in the previous

00:01:21.690 --> 00:01:25.050
lesson, we learned how resizing works. First,

00:01:25.050 --> 00:01:27.860
we have an empty array with 0 elements.

00:01:28.240 --> 00:01:32.640
After the first push, the new array with one element will be created, and this

00:01:32.640 --> 00:01:38.530
new subject will initialize that element with the move constructor. Then the

00:01:38.530 --> 00:01:43.400
second element is pushed, which means that another array with two elements is

00:01:43.400 --> 00:01:48.090
created. After this second element is initialized, the first element from the

00:01:48.090 --> 00:01:53.150
old array is moved to the new array, hence the two move constructor calls.

00:01:53.940 --> 00:01:57.400
Finally, the same thing happens with the third element.

00:01:57.410 --> 00:02:01.580
Once we push it, another array is created, and all of the elements

00:02:01.580 --> 00:02:04.260
from the old array are moved to the new one.

00:02:05.140 --> 00:02:10.020
So with each push, the number of move constructor calls will increase

00:02:10.030 --> 00:02:13.960
because a new, bigger array has to be allocated in memory.

00:02:15.040 --> 00:02:18.520
I, again, have to mention that this would be a lot worse if we didn't

00:02:18.520 --> 00:02:23.600
define move semantics. But we can still make this better if we can at

00:02:23.600 --> 00:02:26.560
least assume how big the vector is going to be.

00:02:26.940 --> 00:02:30.170
Maybe we don't know the exact number, but we know that

00:02:30.170 --> 00:02:32.460
we'll need at least three elements.

00:02:32.840 --> 00:02:35.050
I can use the reserve function to reserve the

00:02:35.050 --> 00:02:37.760
capacity for three elements on the heap.

00:02:38.140 --> 00:02:42.620
Reserve function will just allocate enough memory to store an array of three

00:02:42.620 --> 00:02:46.360
subjects on the heap, but it will not initialize them.

00:02:46.740 --> 00:02:50.190
This is the difference between the reserve and resize function.

00:02:50.190 --> 00:02:54.110
Resize will initialize the memory with empty objects, but

00:02:54.110 --> 00:02:56.680
reserve will not call any constructor.

00:02:56.690 --> 00:02:59.550
It will just reserve the specific amount of memory.

00:02:59.940 --> 00:03:03.420
That means that the vector will not be resized until we

00:03:03.420 --> 00:03:06.500
push three elements because we already have a space in

00:03:06.500 --> 00:03:08.550
memory large enough to store them.

00:03:09.040 --> 00:03:13.110
You should know the difference between the vector size and capacity.

00:03:13.120 --> 00:03:16.350
Let's use these functions before and after the reservation

00:03:16.350 --> 00:03:22.170
of memory. And I will also use them after we push all of

00:03:22.170 --> 00:03:23.660
the elements to the vector.

00:03:24.840 --> 00:03:28.580
Vector's capacity is the amount of allocated memory on the heap.

00:03:28.590 --> 00:03:30.360
So if the capacity is five,

00:03:30.670 --> 00:03:33.440
that means that we can push five elements on the internal

00:03:33.440 --> 00:03:36.360
array until we have a need to resize it.

00:03:36.740 --> 00:03:40.550
Size is the number of elements on the vector, the elements,

00:03:40.550 --> 00:03:43.460
which were constructed and initialized.

00:03:45.040 --> 00:03:51.120
If I compile this, you can see how capacity and size change. First,

00:03:51.120 --> 00:03:54.060
they are both 0 because we have an empty vector.

00:03:54.940 --> 00:03:58.090
Then we reserved enough memory to store three subjects.

00:03:58.090 --> 00:04:02.970
So the capacity is 3, but there are no elements, so the size is still 0.

00:04:04.140 --> 00:04:06.440
But after we push all of the elements,

00:04:06.450 --> 00:04:09.630
the capacity is filled up with these new objects, so

00:04:09.630 --> 00:04:11.960
the size is equal to capacity.

00:04:12.740 --> 00:04:15.950
This is a great way to optimize the program because now the

00:04:15.950 --> 00:04:19.390
vector has enough capacity, so it doesn't have to create new,

00:04:19.390 --> 00:04:22.250
bigger arrays and move all of the elements.

00:04:23.240 --> 00:04:26.530
That is why the number of move constructors is no longer

00:04:26.530 --> 00:04:29.660
increasing with each push to the back of the array.

00:04:30.340 --> 00:04:34.900
So the capacity is always either equal to the size or larger,

00:04:34.910 --> 00:04:38.470
but never smaller because we cannot store elements if we don't

00:04:38.470 --> 00:04:40.260
have enough memory to store them.

00:04:41.040 --> 00:04:44.060
I can remove this capacity and size output for now,

00:04:44.840 --> 00:04:47.660
but we can make this program even more efficient.

00:04:48.040 --> 00:04:48.880
For example,

00:04:48.880 --> 00:04:52.290
why do we even construct these temporary subjects in the main function

00:04:52.290 --> 00:04:56.350
scope if we know that we are just going to move them to the array anyway?

00:04:56.740 --> 00:04:59.140
So instead of using this push_back function,

00:04:59.150 --> 00:05:01.720
let's call the emplace_back function.

00:05:01.730 --> 00:05:07.230
This alternative function will take in the constructor parameters and construct

00:05:07.230 --> 00:05:11.760
the object by using these parameters directly on the vector.

00:05:12.140 --> 00:05:16.430
That means that we don't have to create temporary subjects in the main scope.

00:05:16.440 --> 00:05:21.850
We create them right on the back of the array. And if I now compile this,

00:05:21.850 --> 00:05:25.090
you can see that we no longer have the need for move constructors

00:05:25.090 --> 00:05:28.860
because the parameterized constructors are creating objects with the

00:05:28.860 --> 00:05:32.160
values from the emplace_back function argument.

00:05:32.740 --> 00:05:36.610
If you're trying to add prvalues to the vector, emplacing is

00:05:36.620 --> 00:05:38.950
always a better solution than pushing.

00:05:39.840 --> 00:05:43.310
There is also a pop_back function, which does the opposite.

00:05:43.320 --> 00:05:45.960
It removes the last element from the vector.

00:05:46.340 --> 00:05:51.760
I will print size and capacity again before and after the pop function.

00:05:53.540 --> 00:05:55.880
Of course, since the element is removed,

00:05:55.890 --> 00:06:00.050
the size will decrease, but the capacity might stay the same.

00:06:00.440 --> 00:06:04.780
This behavior depends on the compiler, but on most systems the capacity will

00:06:04.780 --> 00:06:09.620
still stay the same because if it didn't, vector would always have to allocate

00:06:09.620 --> 00:06:13.240
smaller capacity and move all of the elements there,

00:06:13.250 --> 00:06:14.860
which is not efficient.

00:06:15.340 --> 00:06:18.800
So in most cases, when you resize the array to a smaller size,

00:06:18.810 --> 00:06:21.100
the capacity will not be changed.

00:06:21.110 --> 00:06:23.950
But as I said, this is not a guarantee.

00:06:25.240 --> 00:06:28.230
Clear function will remove all of the elements from

00:06:28.230 --> 00:06:30.360
the array. It will make it empty.

00:06:31.140 --> 00:06:31.960
In this case,

00:06:31.970 --> 00:06:34.560
it can also be assumed that the capacity will still

00:06:34.560 --> 00:06:37.560
stay as it is, and the size will be 0.

00:06:37.940 --> 00:06:41.710
The way vector handles this is even more efficient than you think

00:06:41.720 --> 00:06:44.940
because it doesn't actually remove these elements from the array.

00:06:44.940 --> 00:06:47.960
It just marks them as nonexistent.

00:06:48.340 --> 00:06:51.540
That means that when we try to push new elements in the future,

00:06:51.540 --> 00:06:55.360
they will overwrite the old elements, which are not considered

00:06:55.370 --> 00:06:57.260
as a part of the vector anymore,

00:06:57.270 --> 00:07:02.160
so no copying or moving of the old elements is required.
