Proper handling of dynamic memory is a fundamental aspect of effective C++ programming. The new and delete operators provide fine-grained control over memory allocation and deallocation, but their misuse can result in serious issues such as memory leaks, dangling pointers, undefined behavior, and application crashes. This article offers dynamic memory management in C++, highlighting the operational semantics of new and delete, common errors encountered during manual memory handling, and best practices to reduce risks.
Table of Contents:
What is Dynamic Memory Allocation?
In C++, Dynamic Memory allocation means allocating memory during run time. This is particularly convenient when the size of a data structure (such as an array) is not known beforehand. In contrast, static memory allocation occurs at compile time with fixed sizes, such as for global or local variables.
Note: Static allocation occurs during compile time (e.g., global or local variables), while dynamic memory is managed via pointers. Memory can be allocated and deallocated during runtime using operators like new and delete.
new Operator
In C++, the new operator is used to allocate memory dynamically. It returns a pointer to the start of the allocated memory block. If you declare a single variable with new, C++ will automatically call the object’s constructor, if it is a class type. For instance:
Example:
Output:
This code uses new to allocate memory for a single integer. It stores the value 50 into that memory and prints it. At last, to free the allocated memory, the delete is used.
Allocate a Block of Memory (Array)
To allocate memory for an array, you can use the new operator with square brackets:
Example:
Output:
Allocates an array of 5 integer elements using dynamic memory allocation. Loop is used to populate and print the values in the array. Finally, to free the allocated memory, the delete is used.
delete Operator
You use the delete operator to deallocate that memory, which was allocated with the new operator.
Example:
Output:
The above code demonstrates the dynamic memory allocation for both single values and arrays. The delete [] operator shows the proper cleanup to prevent memory leaks.
What if Enough Memory is Not Available During Runtime?
When new fails, it means that the system is unable to provide the requested amount of memory (because it simply does not have enough resources) and it throws an std::bad_alloc exception. You can do this with try-catch blocks:
Example:
Output:
This code demonstrates two ways to handle memory allocation failure in C++, using a try-catch block to catch std::bad_alloc exceptions and using std::nothrow to prevent exceptions and check for a null pointer instead.
Errors Associated with Dynamic Memory
Here’s a list of errors associated with dynamic memory in C++.
- Memory Errors: The errors are caused by accessing deleted memory.
- Memory Leaks: Occurs when memory is allocated but never deallocated.
- Double Deletion: If you delete a pointer more than once, the behaviour is undefined.
- Invalid Delete: Using delete on memory that was not allocated using new, or delete[ ] when it was allocated by new, and vice versa.
- Mixing malloc()/free() with new/delete: Leads to undefined behaviour due to incompatible allocation mechanisms.
Let’s discuss some common errors with examples in C++:
1. Dangling Pointers
A dangling pointer is a pointer that points to a memory location that is already free. Dereferencing such pointers results in undefined behaviour:
Example:
Output:
This can be best understood with the help of the above code that allocates memory using new for an integer and assigns the value 10. After calling delete, the pointer ptr is now a dangling pointer, because it still holds the address of the memory that has been freed.
2. Memory Leaks
A memory leak occurs when memory allocated using new is not properly deallocated using delete. This leads to wasted memory, especially for longer-running programs or loops, as the unreleased memory is not available to the system to be reused.
Example:
Output:
In the above example, the new int[100] allocates the memory on the heap, but the delete[] ptr is missing; Hence, there is no chance of the memory getting released. This may lead to a memory leak.
3. Mixing new/delete with malloc()/free()
- malloc() is a C standard library function. It allocates some bytes (a specified number of bytes) from the heap, returning a void* (pointer to memory).
- It will not invoke constructors in C++. The delete operator in C++ deallocates memory allocated with new, and also calls the object destructor.
- malloc() does not ensure type safety and object lifecycle management in C++, while delete guarantees proper C++ object cleanup. Thus, the reason mixing malloc() and delete is a bad idea is that delete expects memory allocated with new, whereas malloc() allocates memory allocated with malloc() (obviously).
- So if you pass malloc() memory to delete, the program won’t delete the space you allocated (hence, undefined behaviour from now on). That is why malloc()/free() should always be coupled together, and new/delete should be coupled together.
Feature |
new / delete |
malloc() / free() |
Language |
C++ |
C (also usable in C++) |
Allocates memory |
Yes |
Yes |
Calls constructor |
Yes |
No |
Calls destructor |
Yes |
No |
Type safety |
Returns a typed pointer |
Returns void* |
Can be overloaded |
Yes |
No |
Usage in C++:
Output:
Dynamically Allocate Memory Using New and Release using delete. Here, in the new MyClass(), memory for the object is allocated, and the constructor is called; The output is “Constructor called”. delete obj; Delete is used to free up the memory and call the destructor, which will print “Destructor called.”
Get 100% Hike!
Master Most in Demand Skills Now!
Usage in C:
Output:
The above code uses malloc() in C. It checks whether the allocation was successful, assigns values to the arrays, prints them, and frees them to properly clear the memory to avoid any memory leaks.
Custom Memory Allocation in C++ using Overloaded new and delete
In C++, you can overload the new and delete operators to customise the way memory is allocated and deallocated. This allows you to implement custom memory management strategies, such as tracking memory usage, debugging memory leaks, or optimizing memory allocation for specific use cases. Overloading new and delete can be helpful when you need more control over how memory is handled in your program.
Example:
Output:
In C++, you can overload the new and delete operators. By overloading these operators, you will be able to include custom behaviour such as logging memory usage or handling allocation failures.
Memory Management in Classes: Rule of Three, Five, and Zero
In C++, this practice of memory management in classes is called the Rule of Three, Five, and Zero. The Rule of Three states that if a class needs a user-defined destructor, copy constructor, or copy assignment operator, it most likely needs all three. Specifically, the Rule of Five expands this concept to also consider the move constructor and move assignment operator, which help to manage the resources efficiently. The Rule of Zero states that you should try and design your classes so that you do not have to create any of these by using RAII and smart pointers to automatically manage the resource.
Example:
Output:
When a class allocates dynamic resources, the Rule of Three refers to the need for that class to define a destructor, copy constructor, and copy assignment operator. This avoids things like shallow copies and memory leaks.
Conclusion
The allocation is more flexible and can be done at runtime using new and delete when it comes to dynamic memory allocation in C++. All that said, it is important to use them correctly to prevent things such as memory leaks and dangling pointers. To write safer and cleaner code, you can use some advanced techniques like overloading new/delete and following the Rule of Three/Five/Zero. By using memory wisely, they can become more efficient and robust programs.
New and Delete Operators in C++ – FAQs
Q1. What is dynamic memory allocation in C++?
In general, dynamic memory allocation means allocating memory during execution via the new and delete operators.
Q2. What is the difference between new and malloc()?
new calls constructors and is type-safe, while malloc() is a C function and doesn’t call constructors.
Q3. When should I use delete[] instead of delete?
If you want to allocate the arrays using new[], use delete[].
Q4. What happens if I forget to deallocate memory?
It leads to a memory leak, which can gradually cause performance degradation.
Q5. Can I mix malloc() with delete or new with free()?
No, mixing them will lead to undefined behaviour. Always pair new/delete and malloc/free.