about "delete" and "delete []"

Started by
34 comments, last by xiajia 11 years, 2 months ago

code like this:


int * a = new int(0);
int * b = new int(0);
int * c = new int[2];
int * d = new int[2];
int * e = new int[1];
delete a;
delete []b;
delete c;
delete []d;
delete e;

The compiler does not prompt "error" or "warning"(vs 2005 at warning level 4).
But such use of "delete" and "delete []" is incorrect according the book(line "delete []b;" and "delete c;").
Is that so?what problem will cause?

Advertisement

Well, it's undefined behaviour, so technically, while there is no guarantee it will work, there is no guarantee it will fail, either.

I suggest using it the right way, the reason it worked here is probably because int is a simple data type. If you were using a more complicated class, the new[] will create each element of the array as expected, and the delete[] will call the destructor of each element, whereas the delete would just free the array without calling the destructor for each element, which may result in a memory leak and possibly much worse errors. Again, just because it works, doesn't mean it's correct.

Also, I suppose the lack of compiler warnings comes from the fact that it's not always possible to distinguish an array from a pointer to a single object, but I could be wrong on this.

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”

Is that so?what problem will cause?

Maybe nothing. Maybe a memory leak. Maybe random memory corruption anywhere in your program. Maybe an instant crash.

Always make sure you use the correct 'delete' code depending on what kind of 'new' code was used.

That sort of thing can't be detected at compile-time, in the general case.
Consider:

void foo(int x)
{
    int *p;
    if ((x % 2) == 0)
        p = NULL;
    else if ((x % 3) == 0)
        p = new int(0);
    else if ((x % 5) == 0)
        p = new int[2];

    if ((x % 7) == 0)
        delete p;
    else if ((x % 11) == 0)
        delete[] p;
}
This will produce well-defined behaviour if I pass in say 4, 14, 21, 22, or 55, but will produce undefined behaviour if I pass in say 7, 33 or 35, and will leak memory if I pass in say 3.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

delete what you new, and delete [] what you new [].

Welcome to the land of C++, where you can completely shoot your leg off and introduce massive bugs without any errors from the compiler. The compiler doesn't force you to Do The Right Thing (and it many cases, it can't deduce what the Right Thing is to enforce it even if it wanted to). It assumes you know what you should and should not do, and only enforces a very minimal set of rules (the other rules aren't enforced, but you're still expected to follow them, like this rule we're talking about here).

If you don't follow these rules, anything can happen, from your program continuing to work as if everything was fine (this is the worst case, because it hides the error), to crashing the program (this is the best case, because now it's obvious there's a problem), to a black hole opening up and swallowing us all (ok, maybe this is the worst case). It's called "undefined behavior."

Now, as for why you have to use delete [] when you use new [] (and can't just use delete for everything), the main reason that I can think of is memory book keeping by the compiler. How does the system know how much memory to free when you say delete (or delete [])? There's some book keeping of memory done behind the scenes, and the compiler is allowed to do book keeping however it wants, pretty much. It's entirely possible that the compiler decides to use different types of book keeping, depending on whether you used new or new []. Thus, you need to make sure the right book keeping is undone when you call delete.

In combination with the book keeping described above, when you destroy an object its destructor gets called. delete signals that there is only one object being destroyed, and thus only one destructor needs to be called. However, delete [] signals that there are one or more objects being destroyed, and thus potentially many destructors need to be called. Mixing the two incorrectly is just asking for some bad mojo.

There might be additional reasons as for why its important to match delete with new and delete [] with new [], but I'd consider these as the important base reasons why.

Ultimately, though, you can sum it up and just say "because the C++ standard says so."

[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

the delete[] will call the object's destructor function

i use new[ ] and delete [ ] when i need to create and delete an allocated array

the delete[] will call the object's destructor function
The previously posted answers were meant just as much for you as for the original topic-poster.
Read them. Learn from them.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

The only difference between delete and delete[] is, that in the latter case the object destructor isn't called for the pointer address only but all subsequent "positions" in the array until the end of the memory block is reached. In theory it should be save to always call delete[] since the memory block would have only sizeof(object). But it's better to follow the rules to ensure compiler compability. You should also consider using dynamic arrays like std::vector which does the gritty things for you.

Let's talk one common implementation and what exactly happens when you mix delete and delete [] with this kind of implementation.

Let's say you allocate a single std::string object with regular new. The implementation will use ::operator new to allocate sizeof(std::string) bytes, construct the object in that space, and return a pointer to the newly constructed object (which will be the same as the pointer obtained from ::operator new). When things are deleted later with scalar delete the destructor is called on the pointed to object and the memory returned with ::operator delete.

Now let's say you allocate an array of N std::string objects with new []. When you call delete [] later the compiler needs to figure out how to call the destructor for N std::string objects. What happens with one exceptionally common implementation is that new [] actually allocates memory for N * sizeof(std::string) + sizeof(size_t) (along with some possible fudge factors do to alignment or what not) with ::operator new. It stuffs the number of elements in the array in the first sizeof(size_t) bytes and constructs the actual elements in rest of the allocated memory and returns a pointer to the first element, which is not the pointer obtained from ::operator new. When delete [] is called later, it takes the pointer you pass it, subtracts the size of the count from the pointer and uses that information to determine how many objects to destroy. The memory is then freed by passing the subtracted pointer to ::operator delete.

So what happens when you use scalar delete on a std::string array allocated with new []? It destroys the first element in the array, without calling the destructor for any of the other elements in the array and then passes the address of that element to ::operator delete, which is not the same pointer that came from ::operator new. Boom.

What happens when you use delete [] on a single std::string object allocated with new? It tries to interpret the memory preceeding the allocated object as a count and then calls the destructor for that many objects, and then passes the address of that count to ::operator delete, which is still not the same pointer that came from ::operator new. Boom.

Mixing delete and delete [] isn't safe in either direction.

This topic is closed to new replies.

Advertisement