views:

620

answers:

8

I came across this kind of code once in a while - I suspect the creator is/was afraid that table delete would iterate over the table and "cost performance" (which imho will not be done either way)... is there any real benefit one might get/consider/imagine from not using the the table delete here?

myClass** table = new myClass* [size];
... //some code that does not reallocate or change the value of the table pointer ;)
delete table; // no [] intentionally
+7  A: 

That is a memory leak. A new [] must be matched by a delete []. Further, since table is a pointer to the first element of an array of pointers, any array member, if it's an array by itself will need to be de-allocated using a delete [].

dirkgently
unfortunately there's no memory leak - for built in types the table new and delete practicaly map to malloc and free calls (there's no difference if you use table delete or not as the desctructors don't need to be called either way) - I say unfortunately as I'd prefere this kind of hacks not to work at all :)
RnR
@RnR: That's entirely implementation dependent... and certainly not always the case.
SoapBox
Where do built-in types come from? Your example consists of myClass. And yes, you are invoking UB as mentioned by Neil.
dirkgently
@Soap: Yup - I wonder why they allowed "undefined behavior" where in reality they should demand the compiled to issue erros :/
RnR
It does not qualify as a hack. It's plain wrong.
dirkgently
@dirkgently: raw pointers in c++ are practicaly build in types - at least that's what experience (for example writing custom allocators and analyzing the standard ones) shows - there's no constructors, destructors etc - a raw pointer is not much different then an int.
RnR
@dirkgently - I agree - I'm mostly gathering amunition here as I'm going to change that line soon - all your help is really helpfull and welcome :)
RnR
@RnR It is not really possible for the compiler to detect this error
anon
@Neil: the compiler couldn't catch it, but the run-time system could.
Mark Ransom
@Mark Ransom: C++ doesn't have the run-time catch much of anything. That's sort of the point of C++. If you can't catch it at compile time, then it's the programmer's problem.
Michael Kohne
The point of UB is that a diagnostic is not required. This is because you cannot generate a reliable one in the first place.
dirkgently
(Also, this is getting downvoted left and right, will the downvoter(s) put a note?)
dirkgently
+18  A: 

If you do this, you will get what the C++ Standard calls undefined behaviour - anything could happen.

anon
+1  A: 

that statement will leave all of the myClass objects that were pointed to by all the pointers in the array hanging around in memory. It also leaves the array of pointers in memory. There is no way that can be helpful, as it only frees up 32 bits of memory, and the OS still thinks you have (size) myClasses and pointers to each in use. This is just an example of a programmer not cleaning up after themself properly.

Scott M.
It should be noted that a delete [] is still going to leave all of the myClass objects hanging because delete [] of table just gets rid of the pointers. The programmer still has to delete the things-pointed-to for himself.
Michael Kohne
+8  A: 

Not only is there no benefit, the code is just plain wrong -- at best, it leaks memory, and at worst, it can crash your program or open up a hard-to-find security hole. You must always match new with delete and new[] with delete[]. Always.

Adam Rosenfield
+5  A: 

It's definitely wrong as a s new[] needs to be paired with delete[]. If you don't you will get undefined behavior.

It may work (partially), because most implementations use new to implement new[]. The only difference for such an implementation would be that it would only call 1 destructor (for the first element instead of all destructors. But avoid it as it is not legal c++.

lothar
+1  A: 

In theory you should call delete [].

EDIT: The following applies only to Microsoft Visual C++ (I should have said this).

In practice, in Microsoft Visual C++ , it doesn't matter which delete you use when the objects in the array don't have destructors. Since you have an array of pointers, and pointers can't have destructors, you should be OK.

However, as others have pointed out, it is incorrect C++ to mix new [] and delete without []. Although it may work in Visual C++ in this case, the code is not portable and may fail in other compilers.

But going back to the specific case of Visual C++, even if you call delete [], the compiler will realize that it doesn't need to iterate through the array calling destructors when it's an array of primitive types like int, char, or pointers. Calling delete in that case actually works and won't break anything. It would not be slower to do the right thing and call delete [], but it won't be faster either.

In fact, in MSVC++, delete[] p immediately calls the regular operator delete(void *p) when p is a pointer to a simple type, or one without destructors.

Those who don't believe me, step through this code into the CRT code for the first two calls to delete[].

#include "stdafx.h"
#include <malloc.h>
#include <iostream>
using namespace std;

class NoDestructor
{
    int m_i;
};

class WithDestructor
{
public:
    ~WithDestructor()
    {
     cout << "deleted one WithDestructor at " << (void *) this<< endl;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    int **p = new int *[20];
    delete [] p;

    p = (int**) malloc(80);
    free(p);

    NoDestructor *pa = new NoDestructor[20];
    delete [] pa;

    WithDestructor *pb = new WithDestructor[20];
    delete [] pb;

    return 0;
}
Carlos A. Ibarra
It really doesn't matter if we believe you. I'm sure that on that particular compiler it's the case. The real issue is that it doesn't *have* to be the case. MS is allowed to change that behavior at any time. using delete instead of delete[] on something that was allocated with new[] is simply incorrect.
Evan Teran
Fine, I am not advocating the mixing of the two. You should not mix them. I am just answering the original question as to whether it would have any bad effects like memory leaks or performance hits. In practive, on MSVC++, for this type of array, it has no bad effect.
Carlos A. Ibarra
@Thinkcube - you are right that you're answering the correct question and yes - as we can see there's "no difference" in performance in this case but the code works "by accident". Thanks
RnR
+1  A: 

Check with the section [16.11] "How do I allocate / unallocate an array of things?" and beyond in C++ FAQ Lite,

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.11

They explain that the array delete is a must when an array is created.

The instanced of myClass pointed to by the elements of your array should also be deleted where they are created.

eel ghEEz
I'd say point 16.13 is even more to the point - and it seems there MIGHT be a case (not this one probably) where such "intentional" lack of [] might be "needed" - it would be if someone did not override the new and delete operators correctly in the first place and wondered why it's not called or how to get it called ;)
RnR
+2  A: 

There's really no reason to write like that and a serious reason to never do so.

It's true that for types with trivial destructors (like raw pointers in your case) there's no need to know the actual number of elements in the array and so the compiler might decide to map new[] and delete[] onto new and delete to reduce the overhead. If it decides this way you can't stop it without extra steps taken, so this compiler optimization will take place without your notice and will be free.

At the same time someone using your code might wish to overload the global operators new and delete (and new[] and delete[] as well). If that happens you run into big trouble because this is when you may really need the difference between the delete and delete[].

Add to this that this compiler-dependent optimization is unportable.

So this is the case when you get no benefits displacing delete[] with delete but risk big time relying into undefined behaviour.

sharptooth
Thanks for summing up the thread very nicely :)
RnR
As an added note, if one really, really wants to not have that overhead, a portable approach would be to use malloc() and free() instead of new[] and delete[]
bdonlan