What is wrong with using delete instead of delete[]?
Is there something special happening under the covers for allocating and freeing arrays?
Why would it be different from malloc and free?
What is wrong with using delete instead of delete[]?
Is there something special happening under the covers for allocating and freeing arrays?
Why would it be different from malloc and free?
Objects created with new[]
must use delete[]
. Using delete is undefined on arrays.
With malloc and free you have a more simple situation. There is only 1 function that frees the data you allocate, there is no concept of a destructor being called either. The confusion just comes in because delete[]
and delete look similar. Actually they are 2 completely different functions.
Using delete won't call the correct function to delete the memory. It should call delete[](void*) but instead it calls delete(void*). For this reason you can't rely on using delete for memory allocated with new[]
[16.13] Can I drop the
[]
when deleteing array of some built-in type (char, int, etc)?No!
Sometimes programmers think that the
[]
in thedelete[] p
only exists so the compiler will call the appropriate destructors for all elements in the array. Because of this reasoning, they assume that an array of some built-in type such aschar
orint
can bedelete
d without the[]
. E.g., they assume the following is valid code:void userCode(int n) { char* p = new char[n]; ... delete p; // ← ERROR! Should be delete[] p ! }
But the above code is wrong, and it can cause a disaster at runtime. In particular, the code that's called for
delete p
isoperator delete(void*)
, but the code that's called fordelete[] p
isoperator delete[](void*)
. The default behavior for the latter is to call the former, but users are allowed to replace the latter with a different behavior (in which case they would normally also replace the corresponding new code in operatornew[](size_t)
). If they replaced thedelete[]
code so it wasn't compatible with the delete code, and you called the wrong one (i.e., if you saiddelete p
rather thandelete[] p
), you could end up with a disaster at runtime.
Why does delete[] exist in the first place?
Whether you do x or y:
char * x = new char[100];
char * y = new char;
Both are stored in char * typed variables.
I think the reason for the decision of delete, and delete[] goes along with a long list of decisions that are in favor of efficiency in C++. It is so that there is no enforced price to do a lookup of how much needs to be deleted for a normal delete operation.
Having 2 new and new[] seems only logical to have delete and delete[] anyway for symmetry.
The difference is that delete will only delete the entire memory range, but will only call the destructor for 1 object. delete[] will both delete the memory and call the destructor for every single object. If you do not use delete[] for arrays, it's only a matter of time before you introduce a resource leak into your application.
EDIT Update
According to the standard, passing an object allocated with new[] to delete is undefined. The likely behavior is that it will act as I described.
When you use new[] to allocate an array, you are actually telling c++ the size of the array. When you use malloc, you are instead telling it how much memory is allocated. In the former case, freeing based on the size of the array would not make sense. In this case, it does. But since there is no difference between a pointer for an array vs. for a single object, a separate function is needed.
new and delete are different from malloc and free in that malloc and free only allocate and free memory; they don't call ctors or dtors.
The reason for this requirement is historical and because new type
and new type [size]
return different things that need to be cleaned up differently.
Consider this code
Foo* oneEntry = new Foo;
Foo* tenEntries = new Foo[10];
These both return a Foo* pointer, the difference is the second call will result in the Foo constructor being called 10x, and there being roughly 10x as much memory.
So now you want to free your objects.
For a single object you would call delete - e.g. delete oneEntry
. This calls the objects destructor and and deallocates the memory.
But here's the problem - oneEntry and tenEntries are both just Foo pointers. The compiler has no idea whether they point to one, ten, or a thousand elements.
When you use the special syntax of delete []
. This tells the compiler "this is an array of objects, figure out the count and then destruct them all".
What really happens is that for new type [size]
the compiler secretly stores 'size' somewhere else. When you call delete[] it knows that this secret value exists so it can find out how many objects are in that block of memory and destruct them.
The question you could then ask is "why doesn't the compiler always store the size?"
That's a great question and it dates back to the early days of C++. There was a desire that for built-in types (char, int, float, etc) the following would be valid for C++;
int* ptr = new int;
free(ptr);
int* ptr = (int*)malloc(sizeof(int) * someSize);
delete ptr;
The reasoning behind this was an expectation that people would provide libraries that returned dynamically allocated memory, and users of these libraries would have no way of knowing whether to use free/delete.
This desire for compatibility meant that the size of an array could not be stored as part of the array itself and had to be kept elsewhere. Because of this overhead (and remember, this was back in the early 80's) it was decided to do this book keeping only for arrays and not single-elements. Thus arrays need a special delete syntax that looks up this value.
The reason malloc/free do not have this problem is that they simply deal with blocks of memory and do not have to worry about calling constructors/destructors.
As to the "why" in the title: one of the design goals of C++ was that there wouldn't be any hidden costs. C++ was also developed at a time when every byte of memory still mattered a whole lot more than it does today. Language designers also like orthogonality: if you allocate the memory with new[]
(instead of new
), you should free it with delete[]
.
I don't think there's any technical reason that new[]
couldn't stick an "I'm an array" flag in the header of the memory block for delete
(no more delete[]
) to look at later.
Stroustrup talks about the reasons for separate new
/new[]
and delete/
delete[]` operators in "The Design and Evolution of C++" in sections 10.3 through 10.5.1:
new
and delete
was a solution for this;delete
operator is that there needs to be more information than just the pointer in order to determine if the pointer points to the first element of an array or if it just points to a single object. Instead of "complicating the common case of allocating and deallocating individual objects", the delete[]
operator is used to handle arrays. This fits in with the general C++ design philiosophy of "don't pay for what you don't use".Whether this decision was a mistake or not is debatable - either way has good arguments, but we have what we have.