The Palos Publishing Company

Follow Us On The X Platform @PalosPublishing
Categories We Write About

How to Safely Use new[] and delete[] in C++ (1)

In C++, dynamic memory allocation and deallocation are done using the new[] and delete[] operators. These are crucial for managing memory for arrays of objects, especially when the size of the array is determined at runtime. However, improper use of new[] and delete[] can lead to memory leaks, undefined behavior, or crashes. To use them safely, follow these best practices:

1. Match new[] with delete[]

Whenever you allocate memory using new[], you must deallocate it using delete[]. This is crucial to prevent memory leaks.

Example:

cpp
int* arr = new int[10]; // allocate memory for an array of 10 integers // Do something with the array delete[] arr; // deallocate memory

If you mistakenly use delete instead of delete[] (or vice versa), it can cause undefined behavior, such as a crash or corruption of the heap.

Incorrect:

cpp
int* arr = new int[10]; delete arr; // This is incorrect, leads to undefined behavior.

2. Avoid Double Deletion

Ensure that a pointer is not deleted more than once. Deleting the same pointer twice can corrupt memory management and cause program crashes.

Solution: Set the pointer to nullptr after deleting it.

cpp
int* arr = new int[10]; delete[] arr; arr = nullptr; // This ensures the pointer is no longer pointing to deallocated memory

3. Check for Null Before Deleting

Although it’s not required to check if a pointer is nullptr before using delete[] (since delete[] nullptr; is a no-op in C++), doing so can make your code more readable and safe when working with complex memory management logic.

cpp
if (arr != nullptr) { delete[] arr; arr = nullptr; }

4. Use RAII (Resource Acquisition Is Initialization)

The RAII paradigm is widely recommended in C++ to automatically manage resources like dynamic memory. Instead of manually managing memory with new[] and delete[], consider using smart pointers or containers like std::vector for automatic memory management.

For example, using a smart pointer:

cpp
std::unique_ptr<int[]> arr(new int[10]); // memory is automatically cleaned up when it goes out of scope

Alternatively, a std::vector handles dynamic arrays for you:

cpp
std::vector<int> vec(10); // no need to manually allocate or deallocate memory

5. Avoid Memory Leaks

Memory leaks occur when you allocate memory but fail to deallocate it. Make sure that every new[] has a corresponding delete[] (or use RAII to handle it automatically). In larger codebases, it’s easy to forget to call delete[] in every exit path, so be cautious with exception handling, returns, and control flow.

Example:

cpp
int* arr = new int[10]; // If an exception is thrown here, the array would never be deallocated unless handled correctly try { // Operations that might throw an exception } catch (...) { delete[] arr; throw; // rethrow the exception after cleanup }

6. Use Array Bounds Safely

When accessing elements of a dynamically allocated array, ensure that you do not go out of bounds. C++ does not perform bounds checking on arrays, and accessing out-of-bounds elements leads to undefined behavior.

Example:

cpp
int* arr = new int[10]; arr[0] = 5; // safe access arr[10] = 6; // undefined behavior, out of bounds

7. Properly Handle Complex Types (Destructors)

If you’re using new[] to allocate an array of complex objects (i.e., objects with destructors), ensure that their destructors are correctly invoked. When you delete an array of objects using delete[], the destructors for each element will be called automatically, but only if the objects were properly initialized.

Example:

cpp
class MyClass { public: ~MyClass() { // Destructor code here } }; MyClass* arr = new MyClass[10]; delete[] arr; // The destructor will be called for each element

8. Consider Using Containers Instead

As previously mentioned, C++ containers like std::vector are the safer and more convenient alternatives to raw arrays. They handle dynamic memory management and offer built-in checks to prevent errors like out-of-bounds access.

Example:

cpp
std::vector<int> vec(10); // Vector with 10 integers, no need to manually allocate memory vec[0] = 5;

If you don’t need the flexibility of raw pointers and dynamic memory, prefer containers over manually managing memory with new[] and delete[].

9. Debugging and Tools

When working with dynamic memory, debugging tools can help catch issues related to new[] and delete[]. Tools like Valgrind or AddressSanitizer can detect memory leaks, double deletions, and access violations, helping you write safer code.

Valgrind Example:
Run your program with Valgrind to check for memory leaks:

bash
valgrind --leak-check=full ./your_program

AddressSanitizer:
Compile your code with AddressSanitizer enabled to catch memory issues:

bash
g++ -fsanitize=address -g your_program.cpp -o your_program

10. Prefer Smart Pointers for C++11 and Beyond

Modern C++ (C++11 and later) encourages the use of smart pointers like std::unique_ptr and std::shared_ptr, which automate memory management and make code safer and easier to maintain.

Example:

cpp
std::unique_ptr<int[]> arr(new int[10]); // Automatically deallocates memory when out of scope

std::vector is often a better choice for dynamic arrays because it handles resizing and memory management for you. However, if you need raw arrays and fine-grained control, smart pointers can be a safer option than raw pointers.


By following these practices, you can ensure that memory is managed safely when using new[] and delete[] in C++. However, as a general rule, when you can, prefer modern C++ features like std::vector and smart pointers to reduce the need for manual memory management.

Share this Page your favorite way: Click any app below to share.

Enter your email below to join The Palos Publishing Company Email List

We respect your email privacy

Categories We Write About