views:

1064

answers:

8

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?

+11  A: 

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[]

See this C++ FAQ

[16.13] Can I drop the [] when deleteing array of some built-in type (char, int, etc)?

No!

Sometimes programmers think that the [] in the delete[] 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 as char or int can be deleted 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 is operator delete(void*), but the code that's called for delete[] p is operator 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 operator new[](size_t)). If they replaced the delete[] code so it wasn't compatible with the delete code, and you called the wrong one (i.e., if you said delete p rather than delete[] 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.

Brian R. Bondy
perhaps you could add to the answer the information/links on overriding various new/delete to answer the later question.
Greg Domjan
@Brian Out of curiousity: why the need to distinguish between the two? As in, why does delete[] exist in the first place?
FreeMemory
@FreeMemory: I added more info on this
Brian R. Bondy
+11  A: 

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.

JaredPar
So why is it good practice to delete[] char arrays then? Just to keep the habit?
Michael Myers
Ummm, I'm pretty sure that calling delete on the result from new[] (or vice versa) is undefined behavior. So, it may lead to much worse than just leaking memory.
derobert
@derobert, I'm not 100% sure if it's defined or not but it certainly creates bad behavior.
JaredPar
it is undefined behavior.
MacX.dmg
however, there are some cases where you have to call delete[] on result of new.
Johannes Schaub - litb
@litb: What cases?
Brian
all cases where you new an array :) like typedef int foo[5]; int *p = new foo; /* delete[]: !! */ delete[] p;
Johannes Schaub - litb
well, struct x { ~x(){} }; int main() { x *xp = new x[1]; delete xp; } crashes here, of course, because it passes the wrong pointer to free (xp is offset 4 bytes from the real allocated block): "*** glibc detected *** ./a.out: munmap_chunk(): invalid pointer: 0x08cbd00c ***"
Johannes Schaub - litb
A: 

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.

Brian
A: 

You can also read the sections 16.12 to 16.14: C++ FAQ

Naveen
A: 

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.

John Dibling
Whoever ninja downvoted this, come and answer for your crimes.
John Dibling
+3  A: 

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.

Andrew Grant
A: 

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.

Dan
+3  A: 

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:

  • 10.3 Array Allocation - discusses that they wanted a way to allow arrays of objects to be allocated using a separate scheme from allocation single objects (ie., allocating arrays from a separate store). Adding the array versions of new and delete was a solution for this;
  • 10.5.1 Deallocating Arrays - discusses how a problem with deallocating arrays using just a single 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.

Michael Burr