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 newed Ts 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 - Arraywill lead to two instances thinking they own the same buffer, and undefined behaviour when both try to- deleteit.
 
- deleteand- deletecheck for null, so the- if(m_pData != NULL)in the destructor is redundant.
 
Good luck :)