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:

../../_images/jaanneviite_1_en.png

We form a local vector variable and a reference to it in the function split.

../../_images/jaanneviite_2_en.png

If the function split returns the reference to its local variable for the main program…

../../_images/jaanneviite_3_en.png

…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.