Hello again!
In this video, we are going to start looking at move semantics.
When we implemented the swap() function, we saw that sometimes we can speed things up by exchanging data,
instead of copying objects.
For example, with the string argument, we could just swap over the memory buffers which contain
the data. And that was much faster than copying all the characters from one object to another. Move
semantics is something a bit like that, except it concerns function arguments and function return
values, rather than the internals of the swap implementation.
C++ often uses value semantics, which means there is a lot of data copying going on.
For example, with function calls. By default, arguments are passed by value, which means copying them,
and they are returned by value, which again means copying them.
With the containers in the Standard Template Library.
These copy data into their elements.
You are not allowed to use references, so you cannot have a container of references.
And the reason for that, is that it is too easy to create "dangling" references.
If you store a local variable in a container element and then return from the function, the container
has a reference to something which does not exist.
You can use pointers.
But again, with local variables, you may get dangling pointers.
If you allocate the pointer on the heap, then you have to manage it and you can get all the usual problems,
including memory leaks.
The reason why value semantics was chosen, was that it avoids the need for a garbage collector.
And in the days when C++ was being devised as a very efficient object-oriented language, garbage
collectors were very slow.
However, it does mean a lot of copying of objects, which increases the execution time.
And there have been various attempts to do something about this. In traditional C++,
the compiler can "elide" copies when it is returning temporary and local variables from a function.
And we looked at copy elision earlier on. And modern C++ has "move semantics", which is another attempt
to ameliorate things.
As an example of how much time this can save.
Let's imagine we have this code.
We have a local variable, which is a vector of 1 million strings.
We pass this variable by value into a function, which is perhaps not the best design, but it's only
an example!
And then we never actually use the original variable again in this scope.
So what will happen here?
The function call will cause the copy constructor to be called for 1 million strings.
And at the end of the scope, when the variable is destroyed, there will be 1 million calls to the
string destructor.
So this code goes to lots of trouble to make sure that "vec" is still a valid object after being passed
to the function.
But in fact, it is all wasted, because we never use it again.
So if there was some way to get this data, the 1 million strings, out of the vector and into the
function call argument, then we could save a lot of time.
So in this case, the function call would not cause any memory allocation.
The data in the vector is moved into the function argument. And that will leave this "vec" variable
as an empty vector, and destroying it is going to take no time at all.
C++11 introduced move semantics.
This is an optimisation over copying objects, which can be used in some cases: if the object being copied
is something called an "rvalue".
And in that case we can just move its data into the target, the function argument, instead of copying
it.
So this is a bit like moving files between folders.
If you are in a file manager, and you have some file in a folder, and you want to have this file in a different
folder, you could copy it.
So the operating system will make a new file and copy the data from the old file into the new file.
Or alternatively, you can move the file. And if it is on the same disk, then the actual data will
stay in the same place on the disk, and the operating system will just change the name of the file.
So it now has a different file in a different folder, but the same data.
So some diagrams, finally, just to show you the difference between these three ways of doing things.
So with copying, if we are copying this string2 into string1, each element is going to be copied
into the elements of string1.
With swapping, the two memory buffers of these strings are going to be swapped over. So the buffer
from string1 will end up in string2, and string1 will get the old buffer from string2.
If we move the data from string2 into string1, then string1's buffer will now be the old string2
buffer, and string2 is going to have an invalid buffer.
Okay, so that is it for this video.
I will see you next time.
But until then, keep coding!