views:

353

answers:

13

When, if ever, can delete and free be used interchangeably in C++?

My concern is as follows: Say there is an incorrect mixup in the use of malloc/free and new/delete (not to mention new[]/delete[]). However delete and free doing the same thing fortuitously so this goes uncaught in testing. Later this may lead to a crash in production.

How can I enforce some kind of check to prevent this? Can I be warned if the two are mixed up? If not at compile time, perhaps some code instrumentation at run time? How would I approach this?

Edit: Intention of this question is to find ways to AVOID inadvertent mixup in the usages.

+4  A: 

You should always use new and delete in C++, as only new and delete will call the object's constructor and destructor.

If you find you must use both (for instance, if you're interfacing with a C library), thorough code reviews should carefully scrutinize any uses of free() to determine whether or not they correspond to a malloc(), and whether or not they are being used in a C context.

Nick Lewis
+7  A: 

Never. If it works it's by sheer accident of implementation. Do not rely on this behavior.

JaredPar
That's the point of the question - by accident of implementation, the bug is not exposed in testing. How do I create an infrastructure to catch memory issues like this?
S I
@SI, I'd take Neil's approach then, ban malloc and free in your application altogether.
JaredPar
See David's comment above: It's not always practical to mandate the use of new and delete: I found a C package that would do what we wanted, and wrote a C++ wrapper to make it more usable by our software, and so that particular project is mixed
S I
@S I: Make that package returning smart pointers which take care of freeing the objects in the right way.
sbi
A: 

You should always use delete or delete[] when freeing things allocated with new. The same goes for malloc and free.

If using free for deleting new:ed classes the destructor won't be properly called. Also new and delete doesn't necessarily use malloc/free for its allocations so you might end up corrupting your heap as well.

Laserallan
A: 

One thing I've done relating to this is to reimplement malloc and free in C++, so that it calls new/delete under the hood. It's not optimal, but it serves well enough on a smaller project.

Paul Nathan
and if new calls. malloc you would seem to have an infinite recusrsion
anon
You've also changed your system to be nonstandard. Malloc and free may not be defined in terms of new and delete, although the opposite is OK. Since it's permissible and fairly easy to define new in terms of malloc and placement new, I'd expect the problem Neil pointed out to be fairly common.
David Thornley
how would that even be implemented? Malloc and free aren't aware of the type of their argument, new/delete are. How can free, upon receiving a void*, determine which type is being deleted?
jalf
@David: this wouldn't be a great solution, as I said. It's simply a stopgap solution. E.g., log it to a file. The OP isn't looking for the Be All End All production solution here.@Neil. Then it's not a good solution in that case.
Paul Nathan
+11  A: 

The easy way to not get them mixed up is never to use malloc(), then you will never be tempted to call free(). The infrastructure to create to avoid this problem is called "code review", though in this case a quick "grep malloc(" or "grep free(" on the codebase will probably suffice.

anon
Can you override malloc() and free() to do an assert? Then you could guarantee they are never called. If new/delete call malloc/free though, I'm not quite sure if they'd call the one in the stdlib (since that is what they were linked against?), or if they'd call yours (which wouldn't work), or if you'd get a linker error for multiple definitions of the same function.
KeyserSoze
Of course you can. You can simply create a "malloc macro" that raises a compiler error "Don't use this". Then you need to ensure that you are loading the proper .h file with this new behavior. I've done it in the past to deprecate strcpy etc., so we were forced to use versions that required a length.
Will Hartung
The C (and hence the C++) standard has some strict rules on what you can and cannot redefine. Perhaps some C standardista can comment on malloc/free?
anon
Malloc and free aren't keywords, so I think it's legal to redefine them with macros. It won't be legal for new/delete though.
jalf
@jalf I'm not sure about that. You are not allowed to redefine strcmp() et al, if I remember correctly.
anon
A: 

always use delete for things allocated with new, delete [] for things allocated with new [] and free() for things allocated using malloc().

new and new[] allocate from different heaps than malloc() and using the wrong free()/delete will attempt to deallocate from the wrong heap.

Jeff Leonard
+5  A: 

To answer the second question, if you control both malloc/free and operator new/delete, you can stash extra information to associate with pointers returned by both that tell you how they were allocated. When a pointer is passed to free or operator delete, check to see that it was allocated by the appropriate function. If not, assert or raise an exception or do whatever it is you do to report the mismatch.

Usually this is done by allocating extra memory, e.g., given malloc(size) or operator new(size), you allocate size + additional space and shove extra information in there.

MSN
This is a useful suggestion.
S I
of course, this should only be done for debug/test builds.. you probably don't want that overhead in your production code
Kip
+4  A: 

The only way you can ensure you never get them mixed up is by either:

  • Never using malloc/free in the first place, or
  • Rely on RAII for your memory allocations. Protect every memory allocation in a RAII object which ensures the memory get correctly and consistently freed when it goes out of scope, or wrap the allocation in a smart pointer.

Manually calling delete or free is just an invitation for bugs.

jalf
But something must, at the end of the day, call delete or free. And I think the OP is is talking about low level code here.
anon
Yes, but in a typical RAII object, new and delete are defined near each others, so it's fairly simply to ensure that they don't get mixed up with malloc/free. You only get them mixed up if you call delete/free out "in the wild".
jalf
@Neil: What's wrong with smart pointers? They are the test book example for RAII, and you can specify whatever deleter you need. As as boost/tr1/std::shared_ptr shows, you can even do this at run-time.
sbi
@sbi Smart pointers must be implemented in terms of malloc/free or new/delete. Someone has to write that code.
anon
True, smart pointers may be problematic in this case because they separate allocation (new/malloc) from deallocation (free/delete), so it's possible to end up combining new with free or malloc with delete. Writing your own RAII object would solve it though, as both allocation and deallocation are defined once, at the same time, by the same programmer in the same class.
jalf
A: 

Never mix new/delete with new[]/delete[] or with malloc()/free(). Atop of this, the use of malloc()/free() in C++ is questionable at least.

The easiest way to make sure you never do this accidentally is to not to manage memory manually. In C++ there strings and other containers as well as smart pointers to take care of memory management. There simply isn't a need to do this manually. (I'm not sure whether the last time I remember me typing delete really was the last time I typed it. But if my memory doesn't fail me, this must have been 2001 or 2002.)

sbi
A: 

You could write your own version of the functions which allocate some extra memory in new/new[]/malloc to track which one did the allocation. Then check in delete/delete[]/free that the right function was used to re-claim the memory. You can also check for things like mixing new[] and delete (without the []) this way. You might want to only use these versions in the debug build.

Mike Kale
A: 

You could try running valgrind on your application to see if it catches anything. The manual specifically mentions its ability to catch uses of the wrong deallocation function for a block of memory. I don't think there's a way to do this at compile time, however.

Kristo
Note that in a comment he said he's creating a library, not an application. While you can debug an application, you cannot debug all possible applications your library's users might create.
sbi
A: 

Why dont you just count the total number of malloc statments and tally that with the total count of free? Do the same for New and delete. The process can be automated with regular expressions.

bkm
This is pretty useless. It assumes that there is a one to one mapping of places where something is allocated and where something is de-allocated. That is hardly ever the case.
Tim
int *arr[2];arr[0]=(int*)malloc(sizeof(int));arr[1]=(int*)malloc(sizeof(int));for( std::size_t idx=0; idx<sizeof(arr)/sizeof(arr[0]); ++idx) { free(arr[idx]);}
sbi
+1  A: 

If I had to codify it, I'd put in the style guide something like this:

  • free() may be called only on a private pointer field of an object.
  • malloc()ed buffers (or buffers returned from a C API which caller must free()) must be assigned to a private pointer field of an object.
  • private pointer fields which hold free()-able buffers must only be used for that purpose.
  • if you use hungarian notation, add a letter for it (and if you don't, don't).
  • generally free() will be called only in a destructor, with exceptions when the free()-able buffer is replaced during the lifetime of the object. In that case you can call free() on a value recently copied out of a private field during replacement, rather than on the field value directly.

In other words, stick a wrapper around anything that uses malloc/free. This wrapper could be a single template everyone uses, or you could allow smart pointers with the deletor function set to free(). Then in code review, if you see a call to malloc/free anywhere else, it's wrong.

Basically the best way to stop this being a problem is to be on top of your resource handling in general. In C, people do not have a major problem with accidentally calling free() on streams instead of fclose().

Steve Jessop