Search code examples
c++data-structuresnew-operatordynamic-memory-allocationraw-pointer

Proper way to allocate memory for my own pointer-based array in c++


I've searched for similar questions but I couldn't find any satisfactory for my needs.

I'm a Computer Science student currently studying Algorithms and Data Structures. For my exam, I had to implement a collection of templatized data structures in C++. I was not allowed to use the STL as this was an exam question as to how to implement a library similar to the STL.

My implementation works however I would like to ask you for an advice about dynamic memory allocation.

Some of these data structures use a dynamic array (actually a raw pointer) to store elements, which automatically grows when full and shrinks under a certain load factor threshold (doubling and halving its size, respectively). For the sake of simplicity (and also because I'm not supposed to use them), I didn't use any of "modern stuff" such as smart pointers or move constructor/operator=, and basically I relied on C++98 features. I used new [ ] and delete [ ], but I read everywhere that it is a bad practice.

My question is: what is the proper way to handle dynamic memory allocation for array-based data structures in C++?

Here's an example of what I did (the array has been previously allocated by new [ ]):

template <typename T>
void ArrayList<T>::pushBack(T item) 
{
    if (size < capacity) {  // if there's room in the array
        array[size] = item; // simply add the new item
    } else { // otherwise allocate a bigger array                   
        capacity *= 2;
        T *temp = new T[capacity];
        // copy elements from the old array to the new one
        for (int i = 0; i < size; ++i)
            temp[i] = array[i];
        delete [] array;
        temp[size] = item;
        array = temp;
    }
    ++size;
}

Solution

  • No, you still don't need new and delete. The only reason to still use new in C++ is to perform aggregate initialization, which std::make_unique does not support, and you never need delete at all.

    Your code sample then becomes:

    template <typename T>
    void ArrayList<T>::pushBack(T item) 
    {
        if (size < capacity) {  // if there's room in the array
            array[size] = item; // simply add the new item
        } else { // otherwise allocate a bigger array                   
            capacity *= 2;
            auto temp = std::make_unique<T[]>(capacity);
            // copy elements from the old array to the new one
            for (int i = 0; i < size; ++i)
                temp[i] = array[i];
            temp[size] = item;
            array = std::move(temp);
        }
        ++size;
    }
    

    Which can also be factored down by swapping the two sections:

    template <typename T>
    void ArrayList<T>::pushBack(T item) 
    {
        if (size >= capacity) {  // if there's no room in the array, reallocate                 
            capacity *= 2;
            auto temp = std::make_unique<T[]>(capacity);
            // copy elements from the old array to the new one
            for (int i = 0; i < size; ++i)
                temp[i] = array[i];
            temp[size] = item;
            array = std::move(temp);
        }
    
        array[size] = item; // simply add the new item
        ++size;
    }
    

    Further possible improvements: move the elements when reallocating instead of copying them, use a standard algorithm instead of the manual for loop.