Memory Management Part 2: Managing Dynamically Allocated Memory

Note: Before reading this section, I suggest having a good grasp on pointers or reading through the first part which explains how they work and work with them in a few examples.

Now that pointers have been explained a bit, I can talk about how to manage memory in C++ in a simple manner. Now I will mention that the easiest thing to keep in mind is to make sure that you are managing memory at the right times. Failure to free up unused memory will result in memory leaks whereas freeing up memory too early will result in dangling pointers which can cause major issues for programs. So before I talk about how to mitigate these concerns, I will talk about how to free up spaces of memory.

For most variables in C++, it is not necessary to manage the memory. The reason why I say this is because any variable that is statically allocated will be placed in a memory stack. Once outside of the scope of a method, the memory used in the stack will be freed up. For dynamically allocated variables though this is not the case. When using these, they are placed in the memory heap and are only removed when explicitly told to be removed. For C++, dynamic allocation is typically achieved by using the 'new' keyword and likewise the memory is freed using the 'delete' keyword. In the following example, I will declare a variable, create a pointer to that address, and free the space using the delete keyword:

someClass myObject();
someClass * myPointer = &myObject;
delete myPointer;

Now, as you can see in the example I gave, I am using a fictitious class called "someClass" to show this at work. In the code example below, I show how to put the new entry straight into the pointer:

someClass * myObject = new someClass();
delete myPointer;

Now, in the example below the middleman variable that I was using so to speak has been removed. This uses less lines of code so it will help with overall readability. However, a precaution must be made when doing this. When setting this value, it should not be changed to a different value until it is deleted first. Failing to do this will result in a memory leak, which for a few things may not seem important, but for many variables can add up so it is always good practice to manage memory effectively.

Last but not least, arrays of memory can be allocated in the same manner, however, freeing up memory must also be done as an array. The example code seen below shows how to allocate and free an array of memory respectively:

someClass * myObjects = new someClass[3];
delete[] myObjects;

In the example given, notice that the delete keyword uses square brackets to indicate that this is an array being freed up. This shows that allocating and freeing this memory is not difficult. The major difficulty comes in however with when to free the memory. The best strategy to use is to determine how and when the memory is used so that it is known when it can be freed. This will require running through the program's execution in your mind a bit or possibly some test runs to make sure it works correctly, but it is worth it as managing memory effectively in a program is worthwhile.

I will mention upfront that for people using C++'s Boost Libraries or the new C++11 standard, I would suggest looking at shared_ptr as using these for memory management will automate the removal process and help minimize the possibility of memory leaks and dangling pointers. I would not consider this an end all, be all solution however as there are still plenty of compilers that do not utilize the current C++ standard and when working on older code, understanding memory management will allow possible memory issues to be caught faster.

Now, I know that there are some who use C#, Java, and other languages that utilize garbage collection and they will ask what is the point of learning memory management? Well, I can give a simple answer and that is in some cases understanding memory management and applying these skills to other languages are beneficial. A good example that I saw firsthand was in a C# application I developed. When running the application, garbage collection would not run and elements in C#'s lists were not being removed when the list was overwritten despite all documentation stating to the otherwise. However, I was able to find that the clear method for lists removed the elements, allowing for some control of the memory when used in tandem with telling the garbage collector to run at specific points in time. (Which is usually frowned upon in C#, but in this scenario was beneficial.) So while the examples I gave were in C++, knowing how memory should be used programs being developed and determining discrepancies in how it is being used can be very helpful.