You could use a specialization of your destructor, or tag-dispatch to a deletion function (which would be more flexible). However, this leads to a pretty awkward and brittle design. Supposing you have implemented an equivalent to push_back
, consider these user code snippets:
{
Array<int*> arr;
arr.push_back(new int(42)); // new is on the side of the user
} // ... but delete is not. Weird.
This also leads to a whole class of errors: you sohuld only give new
ed T
s to an Array<T*>
, but there is no security whatsoever against passing in something else:
{
Array<int*> arr;
int i = 42;
arr.push_back(&i); // No diagnostic
arr.push_back(new int[17]); // No diagnostic either
} // Undefined behaviour from calling `delete` on stuff that hasn't been `new`ed
All of this is the reason for the existence of the No raw owning pointers rule: raw pointers should not manage resource lifetime, at all. If I use an Array<int*>
, it should store my pointers and let me use them, but never ever delete
them, because that's trying to manage lifetime.
Instead, if I want an Array
that manages lifetime, I'll use the corresponding smart pointer, with Array<std::unique_ptr<int>>
. This ties in nicely with Array
, requiring it to only call the destructor of the contained objects (which it already does). These destructors (~unique_ptr
, that is) will transitively deallocate the resources as is their job, and all is well.
Additional notes:
Take care to initialize your Array
's buffer if you need -- m_pData = new T[m_nSize];
will allocate default-initialized objects. If these objects have no default constructor, their value will be indeterminate. A simple fix is to use new T[m_nSize]{}
, which will perform value-initialization -- that is, initialize arithmetic types to zero, pointers to nullptr
, and compound types recursively.
Implement copy and/or move semantics for your class, see Rule of three/five/zero. As it is now, copying an instance of Array
will lead to two instances thinking they own the same buffer, and undefined behaviour when both try to delete
it.
delete
and delete
check for null, so the if(m_pData != NULL)
in the destructor is redundant.
Good luck :)