C++ memory allocation
While we are aiming to focus on concepts, there are some underlying language aspects that impact on how we explore memory management. In our case, we need to have a little explore of some mechanics of C++ in terms of how it manages memory, as this will impact on our code here.
C malloc, realloc, and free
C is very low level, which is great as we get to play around with all the mechanics that underlie other languages and tools we encounter. This is what we have looked at so far.
Working with these tools will be fine, as long as we stick to C. If we want to start using C++ features, like string
for example, then we need to think about what C++ is doing on top of these basics.
C++ new, new[], delete, and delete[]
C++ provides a number of operators for performing memory management - with the main categories being new
to allocate memory and delete
to free allocated memory. You use the new
operator with a type, and it allocates space on the heap returning you a pointer to that (much like malloc
). Where you indicate an array, the new operator will allocate space for the indicated number of elements. Similarly, you can use delete
to free the memory allocated to a pointer or delete[]
to free an array. These are shown below, with an integer pointer and array pointer.
Notice there is no equivalent of realloc
. So you can allocate and free, but not change any memory allocation.
Constructors and destructors
Behind the scenes, C++ adds additional features to its memory allocation functions for structs (and classes) to help developers initialise values when they are allocated, and clean up when they are freed. These are called constructors and destructors. Inside the C++ new
operator, C++ will allocate space for the data and then call the constructor to initialise this space. Similarly, the delete
operator will first call the destructor and then free the memory allocation.
If we want to play around with memory allocations, then malloc
, realloc
, and free
are the tools we want to use, but we need to be aware of these extra steps that C++ is performing if we mix in the use of string
.
Mixing malloc and new
Internally, malloc
and new
both allocate blocks of memory. The main difference is the missing call to the constructor when we are working with C++ structs (and classes).
The good news is that C++ provides the capability to call new
on a previous memory allocation using placement new. Using this we can play around with memory using malloc
, and realloc
but still ensure that the constructors are called.
The following code illustrates how we can achieve this.
Mixing free and delete
Freeing the memory allocation has similar issues when we mix C++ with the standard C allocation functions. In C++, the delete
operator will first call the destructor and then free the memory allocation. When using the standard C functions, you must first add a call to the destructor and then use free
to clear the memory allocation.
We can complete the above example by adding in the code to free the memory allocations.
Doing this with arrays
When you use this to work with arrays, you need to make sure to call the constructor and destructor for each element of the array - when allocating or freeing space. The following code demonstrates this with the test struct we have been working with.
Doing this with generics
This also works with generics, with C++ ensuring that there are appropriate destructors and constructors for all primitive types. The following code shows an example of this where we use a generic function to allocate space on the heap, and a generic procedure to free that allocation. Notice that we can use this to allocate space for the struct, but also allocate space for an integer.