Dynamic arrays let us specify the size at runtime.
But what if we don't know how much elements are we going to need in the future?
What if the requirements fluctuate from 0 to 500 elements
based on the usage of the program in question?
Well, of course, we could create an empty vector with 0
elements and then resize it to fit one more element every
time we have a need for a new subject.
But this would first create a new empty subject every time we
resize the vector. I don't want to do that because I already know
that I will initialize these objects later.
Fortunately, vector works kind of like a stack data structure.
So if you want to add a new element, you can just use the push_back function.
This will first resize the vector to fit one more object, and then it
will construct this new object by using the move constructor, which
will move data from this temporary subject.
Let's compile this to see it in action.
So the vector will no longer use the default
constructor to create an empty object.
This new object will be pushed to the back of the
array immediately after the resizing.
However,
you can see that after each push, the number of move constructor
invocations increases. This would make sense because in the previous
lesson, we learned how resizing works. First,
we have an empty array with 0 elements.
After the first push, the new array with one element will be created, and this
new subject will initialize that element with the move constructor. Then the
second element is pushed, which means that another array with two elements is
created. After this second element is initialized, the first element from the
old array is moved to the new array, hence the two move constructor calls.
Finally, the same thing happens with the third element.
Once we push it, another array is created, and all of the elements
from the old array are moved to the new one.
So with each push, the number of move constructor calls will increase
because a new, bigger array has to be allocated in memory.
I, again, have to mention that this would be a lot worse if we didn't
define move semantics. But we can still make this better if we can at
least assume how big the vector is going to be.
Maybe we don't know the exact number, but we know that
we'll need at least three elements.
I can use the reserve function to reserve the
capacity for three elements on the heap.
Reserve function will just allocate enough memory to store an array of three
subjects on the heap, but it will not initialize them.
This is the difference between the reserve and resize function.
Resize will initialize the memory with empty objects, but
reserve will not call any constructor.
It will just reserve the specific amount of memory.
That means that the vector will not be resized until we
push three elements because we already have a space in
memory large enough to store them.
You should know the difference between the vector size and capacity.
Let's use these functions before and after the reservation
of memory. And I will also use them after we push all of
the elements to the vector.
Vector's capacity is the amount of allocated memory on the heap.
So if the capacity is five,
that means that we can push five elements on the internal
array until we have a need to resize it.
Size is the number of elements on the vector, the elements,
which were constructed and initialized.
If I compile this, you can see how capacity and size change. First,
they are both 0 because we have an empty vector.
Then we reserved enough memory to store three subjects.
So the capacity is 3, but there are no elements, so the size is still 0.
But after we push all of the elements,
the capacity is filled up with these new objects, so
the size is equal to capacity.
This is a great way to optimize the program because now the
vector has enough capacity, so it doesn't have to create new,
bigger arrays and move all of the elements.
That is why the number of move constructors is no longer
increasing with each push to the back of the array.
So the capacity is always either equal to the size or larger,
but never smaller because we cannot store elements if we don't
have enough memory to store them.
I can remove this capacity and size output for now,
but we can make this program even more efficient.
For example,
why do we even construct these temporary subjects in the main function
scope if we know that we are just going to move them to the array anyway?
So instead of using this push_back function,
let's call the emplace_back function.
This alternative function will take in the constructor parameters and construct
the object by using these parameters directly on the vector.
That means that we don't have to create temporary subjects in the main scope.
We create them right on the back of the array. And if I now compile this,
you can see that we no longer have the need for move constructors
because the parameterized constructors are creating objects with the
values from the emplace_back function argument.
If you're trying to add prvalues to the vector, emplacing is
always a better solution than pushing.
There is also a pop_back function, which does the opposite.
It removes the last element from the vector.
I will print size and capacity again before and after the pop function.
Of course, since the element is removed,
the size will decrease, but the capacity might stay the same.
This behavior depends on the compiler, but on most systems the capacity will
still stay the same because if it didn't, vector would always have to allocate
smaller capacity and move all of the elements there,
which is not efficient.
So in most cases, when you resize the array to a smaller size,
the capacity will not be changed.
But as I said, this is not a guarantee.
Clear function will remove all of the elements from
the array. It will make it empty.
In this case,
it can also be assumed that the capacity will still
stay as it is, and the size will be 0.
The way vector handles this is even more efficient than you think
because it doesn't actually remove these elements from the array.
It just marks them as nonexistent.
That means that when we try to push new elements in the future,
they will overwrite the old elements, which are not considered
as a part of the vector anymore,
so no copying or moving of the old elements is required.