Introduction to memory management¶
Let us remind ourselves of the assignment (from round 4) where we created
the split
function.
The return value of that function was vector
.
The assignment gave you a tip:
You were not supposed to return a reference
to 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 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 abovementioned 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.
As we proceed to more complicated programs, we will face situations where
it would be better if the programmer was able to determine exactly when
a variable was destroyed.
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 which we will encounter on the next few rounds. 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 much more detailed look at pointers than the first view given at the beginning of the course, when we compared value and reference semantics. The pointer is an important tool in C++ for implementing the higher-level structure of a program (indirect pointing and sharing access/ownership).
On this round, we will create a dynamic data structure, but on later rounds, we are going to use the pointers and dynamic memory management for several other purposes.