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;
}
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.