Introduction to memory management¶
Let us remind ourselves of the assignment
where we created the split function.
That function returned a vector.
The assignment gave you a tip:
You were not supposed to return a reference
to a vector, but the vector itself.
The reason for this tip was that because we have learned to pass large
data structures (such as vector) as (constant) reference parameters
instead of value parameters, you could easily think that you can do the same
with return values.
Even though at first, it might sound smart to increase your efficiency by passing a reference to a vector as a return value instead of passing the vector itself, it does not work. We learned on the previous programming course already that when the execution of a function is finished, all the local variables of the function are removed from the memory of the computer. We could present the above mentioned in this way, for example:
We form a local vector variable and a reference to it in the
function split.
If the function split returns the reference to its
local variable for the main program…
…the reference used by the main program becomes a so-called
dangling pointer because, when the execution of split is finished,
all its local variables are removed from the memory of the computer.
A dangling pointer is a little distracting as a term, because it can mean
either a reference or a pointer targeting a variable that no longer exists.
Returning a reference that points to a local variable is a good example of a mistake you can make as a programmer if you do not understand how the memory management of programming languages works.
Until now, all the variables we have used have been so-called automatic variables, which means that the compiler would find a storage place for them in the memory and take care of destroying them. For example, local variables of a function are destroyed automatically, when the execution of the function ends. The lifetime of an automatic variable is determined based on the code block, where the variable is declared.
As we proceed to more complicated programs, we will face situations where it would be better if the programmer was able to determine the lifetime of a variable. For example, in the above illustrations, it would be useful if the programmer was able to decide how long it is possible to use the contents of the vector.
On this round, we will study manual memory management in C++.
The simplest way to practice this is for you to create a data structure
with a dynamic size (= a data structure that can change in size).
This means that we will implement something that resembles the list of
Python or the STL vector of C++.
The reason for this practice is not a programmer having to implement dynamic containers themself. Actually, this is not even possible in most modern programming languages. Instead the reason is for you to learn the common principles of memory management and to be able to use dynamic memory management for other means. Also, this practice will help us understand, for example, the functioning of the STL containers. An in-depth understanding of memory management helps the programmer to avoid mistakes even if they use pre-made dynamic data structures in their code.
While practicing the use of manual memory management, we will also take a more detailed look at pointers. The pointer is an important tool in C++ for implementing the higher-level structure of a program (indirect pointing and sharing access/ownership).