A memory leak in C++ is a coding problem that occurs if we don't release dynamically allocated memory after it is no longer needed. When memory is allocated but not deallocated, the program consumes unnecessary memory, leading to reduced performance and responsiveness. A memory management flow in our program should look like the following:
To allocate the dynamic memory in C++, we use the new
operator.
int A = new int(5);
To deallocate the dynamic memory in C++, we use the delete
operator.
delete A;
The following diagram will help us understand the allocation and deallocation of dynamic memory:
Note: The unreleased dynamic memory is not permanently wasted, as the system's operating system eventually reclaims it when the program exits, but it can still cause multiple wastage problems during program execution.
Let's discuss some of the reasons that cause memory leaks in C++.
delete
operatorMemory leaks mostly happen when we forget to deallocate the dynamic memory we allocate after we use it.
#include <iostream>using namespace std;int main() {int * A = new int [5];//apply operation on Areturn 0;}
In the code above, we dynamically allocate memory for an integer array A
with a size of 5
elements in line 5. After performing operations on this array, we forget to deallocate it using the delete
operator.
The correct solution for the code above will be to use the delete
operator in line 7 to release the dynamic memory, which is no longer needed.
#include <iostream>using namespace std;int main() {int * A = new int [5];//apply operation on Adelete[] A;return 0;}
delete
operatorIn some cases, we use the delete
operator incorrectly while deallocating the dynamic memory. For instance, if we allocate a dynamic 1D array memory using the new[]
operator, the memory should be deallocated using the delete[]
operator.
#include <iostream>using namespace std;int main() {int * A = new int [5];//apply operation on Adelete A;return 0;}
In the code above, we dynamically allocate memory for an integer array A
with a size of 5
elements in line 5. After performing operations on this array, we delete it in line 7 without the []
square brackets so only the first element of the array is deleted, as the delete
operator without the brackets deletes a single object.
The correct solution for the above code will be to use the delete
operator with the []
square brackets in line 7 to release the dynamic memory, which is no longer needed.
#include <iostream>using namespace std;int main() {int * A = new int [5];//apply operation on Adelete[] A;return 0;}
When an exception is thrown in our code, and the delete
operator is written after the exception-causing statement, our code will skip all the next code and jump directly to the catch
block. As a result, the program loses the opportunity to release the memory, causing a memory leak.
#include <iostream>#include <stdexcept>using namespace std;int main() {try{int * A = new int (5);throw runtime_error("An exception is thrown!");delete A;}catch (runtime_error& e){//Dealing with the error}return 0;}
In the code above, we dynamically allocate memory for an integer A
with the value 5
in line 7, inside the try
block. An exception is thrown in line 8, so the code jumps directly to the catch
block, skipping the statements for deallocating our dynamic memory.
The correct solution for the above code will be to use the delete
operator before any exception-causing statements to be safe.
#include <iostream>#include <stdexcept>using namespace std;int main() {try{int * A = new int (5);delete A;throw runtime_error("An exception is thrown!");}catch (runtime_error& e){//Dealing with the error}return 0;}
Sometimes, memory may become unreachable due to programming errors or unexpected program flow. This can cause memory leaks as the program loses the ability to deallocate that memory.
#include <iostream>using namespace std;int main() {int * A = new int (5);bool condition = false;if (condition) {delete A; // This delete statement will not be executed if the condition is false}return 0;}
In the code above, we dynamically allocate memory for an integer A
with the value 5
in line 5. We release this memory inside a condition in line 8. So whenever the condition is satisfied, our memory will be deleted, which leads to the risk of memory leakage.
The correct solution for the code above will be releasing the memory outside the if
condition in line 6.
#include <iostream>using namespace std;int main() {int * A = new int (5);delete A;bool condition = false;if (condition) {// working in the condition}return 0;}
To avoid memory leaks, we should follow best practices in memory management. Additional C++ features like smart pointers, unique_ptr
, and shared_ptr
can significantly reduce the risk of memory leaks as they automatically manage memory deallocation.
The following code demonstrates how to allocate dynamic memory using unique_ptr
in C++.
#include <iostream>#include <memory>using namespace std;int main() {unique_ptr<int> A(new int(5));//perform operations on dynamic memoryreturn 0;}
In the code above, we first include the header file memory
in our code so we can use the unique_ptr
. Then, we dynamically allocate memory for an integer A
with the value 5
in line 6 using the unique_ptr
. This memory will be automatically deallocated when our main
function is returned.
Memory leaks in C++ occur when dynamically allocated memory is not properly deallocated, leading to the wastage of system resources and potential instability of the program. A memory leak can happen due to various programming errors. We should be mindful of these errors and carefully manage memory to ensure better resource utilization.