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