It is definitely not safe as you can simply try out with the following code:
#include<iostream>
class test {
public:
test(){ std::cout << "Constructor" << std::endl; }
~test(){ std::cout << "Destructor" << std::endl; }
};
int main() {
test * t = new test[ 10 ];
delete t;
return 1;
}
Have a look at http://ideone.com/b8BiQ . It fails misserably.
It may work when you do not use classes, but only fundamental types, but even that is not guaranteed.
EDIT: Some explanations for those of you who want to know why this crashes:
new
and delete
mainly serve as wrappers around malloc()
, hence calling free()
on a newed pointer is most of the time "safe" (remember to call the destructor), but you should not rely on it. For new[]
and delete[]
however the situation is more complicated.
When an array of classes gets constructed using new[]
each default constructor will be called in turn. When you do delete[]
each destructor gets called. However each destructor also has to be supplied a this
pointer to use inside as a hidden parameter. So before calling the destructor the program has to find the locations of all objects within the reserved memory, to pass these locations as this
pointers to the destructor. So all information that is later needed to reconstruct this information needs to be stored somewhere.
Now the easiest way would be to have a global map somewhere around, which stores this information for all new[]
ed pointers. In this case if you delete
is called instead of delete[]
only one of the destructors would be called and the entry would not be removed from a map. However this method is usually not used, because maps are slow and memory management should be as fast as possible.
Hence for the stdlibc++ a different solution is used. Since only a few bytes are needed as additional information, it is the fastest to just over-allocate by these few bytes, store the information at the beginning of the memory and return the pointer to the memory after the bookkeeping. So if you allocate an array of 10 objects of 10 bytes each, the programm will allocate 100+X
bytes where X
is the size of the data which is needed to reconstruct the this.
So in this case it looks something like this
| Bookkeeping | First Object | Second Object |....
^ ^
| This is what is returned by new[]
|
this is what is returned by malloc()
So in case you pass the pointer you have recieved from new[]
to delete[]
it will call all destructors, then substract X
from the pointer and give that one to free()
. However if you call delete
instead, it will call a destructor for the first object and then immediately pass that pointer to free()
, which means free()
has just been passed a pointer which was never malloced, which means the result is UB.
Have a look at http://ideone.com/tIiMw , to see what gets passed to delete
and delete[]
. As you can see, the pointer returned from new[]
is not the pointer which was allocated inside, but 4 is added to it before it is being returned to main()
. When calling delete[]
correctly the same four is substracted an we get the correct pointer within delete[]
however this substraction is missing when calling delete
and we get the wrong pointer.
In case of calling new[]
on a fundamental type, the compiler immediately knows that it will not have to call any destructors later and it just optimizes the bookkeeping away. However it is definitely allowed to write bookkeeping even for fundamental types. And it is also allowed to add bookkeeping in case you call new
.
This bookkeeping in front of the real pointer is actually a very good trick, in case you ever need to write your own memory allocation routines as a replacement of new
and delete
. There is hardly any limit on what you can store there , so one should never assume that anything returned from new
or new[]
was actually returned from malloc()
.