Search code examples
c++memory-managementnew-operatorsmart-pointersdelete-operator

How to know when to call delete and when delete[] in C++?


I was implementing classes my_unique_ptr and my_shared_ptr that mimic the standard library smart pointers std::unique_ptr and std::shared_ptr to get to know it better.

When implementing the destructors I'm in this trouble to decide whether to use delete or delete[] to free the memory. As much as I read on SO and other sites, there is no portable way to know how many bytes were allotted by new[] (or whether new was used or new[]) (How many bytes were allocated by new?)

While implementing class my_unique_ptr I have no way of knowing how many bytes the user will request from the constructor , i.e.

Whether he will do my_unique_ptr<int> ptr1(new int)

or will he do my_unique_ptr<int> ptr1(new int[5])

If there is a way please let me know!

Here is my class (simplified and without cpy/move constructors):

template<typename ValueType>
class my_unique_ptr {

    ValueType *internal_ptr = nullptr;

public:
    
    // Default constructor
    my_unique_ptr() {
       internal_ptr = nullptr;
    }

    // Paramterized constructor
    explicit my_unique_ptr(ValueType *ptr) {
       if (ptr)
        internal_ptr = ptr;
    }

    // Destructor
    ~my_unique_ptr() {
       
        // How do I know whether to use
           delete ptr;

        // or
           delete[] ptr;
        
     }

I read somewhere that the compiler keeps track of the argument of new[] which is then used by delete[] to free the memory correctly. Also read that "It is the responsibility of the programmer to match new with delete and new[] with delete[]". But how do I program this in my class so that it always matches the correct operators?

Sidenote:

Running valgrind while using delete everywhere instead of delete[] and passing more than 1 int (say with new int[5]) in my constructor, Valgrind says that all memory blocks were freed and there is no chance of leak! (although shows warnings like mismatched new[] with delete. Does this mean delete is successfully freeing all the 5 ints allocated by new[]? Please help. I'm on Ubuntu 18.04 and using gcc 7.5.0.


Solution

  • You are correct that you can't know if a memory was allocated by new or new[]. But you can't even know that a memory stores an automatic duration object. E.g. your user can do:

    int a = 24;
    
    my_unique_ptr<int> ptr1(&a); // oups
    

    There is no way for you to know that. So do what the standard library does:

    • make a specialization for array types, e.g. template <class T> class my_unique_ptr<T[]> that calls delete[] on the destructor.

    • It is a requirement that my_unique_ptr<T> must be initialized with a memory obtained from new and my_unique_ptr<T[]> must be initialized with a memory obtained from new[]. This is a contract the users of your library must respect. If it's not respected then it's UB.

    • help the users respect this contract by providing equivalents of std::make_unique. Also see Advantages of using std::make_unique over new operator


    Does this mean delete is successfully freeing all the 5 ints allocated by new[]

    No. Calling delete on a memory not obtained from new is UB.