The differences between free in C and delete in C++?
Asked Answered
B

4

5

I know the free operation in C is to tell the compiler this particular memory block is free for compiler to use for further allocation, but the memory is not released.

What about the delete in C++? the same as free?

Belindabelisarius answered 25/7, 2011 at 13:23 Comment(3)
Yes, it's the same. But you can overload new/delete implementations and do your own thing https://mcmap.net/q/1920718/-writing-garbage-on-free-delete/176769Poker
@karlphilip delete will call the destructor, free won't do that, and that's a pretty huge differenceAvellaneda
You don't tell the compiler, you tell the runtime. And the memory may or may not be actually returned to the operating system depending on implementation-specific conditions. Usually small-blocks are obtained from ever-growing heap (so reused but not returned) while large-blocks are obtained from system as separate entities (using mmap or similar) and returned to system when freed.Withershins
M
22

There are two notions of delete in C++: One is the operator, declared as ::operator delete(void*), which basically only frees up the memory and isn't usually thought about by most programmers. The other is the delete expression, delete p;, where p is a T*. The expression invokes the destructor of the object pointed to by p (and then frees the memory), which is a crucial language feature of C++ that has no analogue in C.

As a rule of thumb, you pair new expressions with delete expressions, and malloc() function calls with free() function calls:

T * p = new T;        // constructor called!
delete p;             // destructor called!

void * x = malloc(5); // just raw memory
free(x);              // freed

Advanced part (not in response to the OP's question)

Dynamic object lifetime in C++ follows this general pattern: Allocate, construct, destroy, deallocate. The standard new expression performs allocation and construction, while the standard delete expression performs destruction and deallocation.

You could write out the process manually:

T * p = (T*)::operator new(sizeof(T));   // allocate raw memory
p = new (p) T;                           // call the constructor ("placement new")

/*...*/

p->~T();                                 // destroy the object
::operator delete(p);                    // deallocate the memory

In fact, if you really wanted to implement Baby's First C++, you could define the operators as just as malloc/free:

void * operator new(size_t n) { return malloc(n); }
void   operator delete(void * p) { free(p); }

The real C++ magic happens by virtue of the new and delete expressions: The standard new expression invokes the constructor (a new expression is the only way to call a constructor in C++!) after allocation, while the standard delete expression invokes the destructor before deallocation.

Why "standard expression"? Well, you can also define and overload many other versions of new and delete operators. However, there is an important asymmetry: While you can use a custom new operator in a custom new expression (generally dubbed "placement new"), there is no equivalent "placement-delete" expression. So whenever you use a custom new expression, you have to manually invoke the destructor before calling the matching custom delete operator:

T * p = new (A, B, C) T;                          // some custom new expression

// Entirely equivalent version:

T * p = (T*) ::operator new(sizeof(T), A, B, C);  // this is your custom overload
T * p = new (p) T;                                // std. placement-new expression calls constructor

/* ---- later ---- */

p->~T();                                          // Must destroy manually!
::operator delete(p, A, B, C);                    // your matching custom overload

Note that there does not exist a custom delete expression delete (A,B,C) p'!

For completeness, the standard placement new operator, whose only purpose is to call a constructor, is mandated by the standard to take the following form:

void * operator new(size_t, void * p) { return p; }

It's matching delete operator is also mandated, name to do nothing:

void operator delete(void * p, void *) { }

You can see in the above general example why this is necessary.

It is important always to overload custom versions of new and delete in pairs! The reason is that if the object construction fails with an exception inside the constructor, then the memory is released by a call to the delete operator that matches the offending new expression.


Second update: To be exception-safe, we have to consider that the constructor of T might throw:

Version 1:

try {
  T * p = new (A, B, C) T;
  /* ... */
  p->~T();
  ::operator delete(p, A, B, C); // automatically invoked if T::T() throws!
}
catch(...) { }

Version 2:

void * addr = ::operator new(sizeof(T), A, B, C);
try {
  T * p = new (addr) T;  // might throw
  /* ... */
  p->~T();
  // ::operator delete(p, addr); // ditto as in (1), but does nothing
}
catch(...) { }
::operator delete(addr, A, B, C);
Microclimate answered 25/7, 2011 at 13:27 Comment(11)
To be more precise, delete doesn't invoke the destructor; but compiler puts the call to destructor before wherever it finds delete operator. By the time you enter the function block of delete, the destructor is already called.Strategy
@iammilind: Your wording is a bit imprecise: If you say ::operator delete(p);, the destructor will not get called. The destructor call is a consequence of the delete expression.Microclimate
The "raw" calling sequence could be written out manually like this: T * p = (T*)::operator new(sizeof(T)); p = new (p) T; p->~T(); ::operator delete(p); You'd be insane to write that :-)Microclimate
@Kerrek: Why don't you add the comments into the question text? It'd be appropriate when you're extending your own answer. As for the explicit sequence, it's important when you are defining some data structures or allocators (as in custom allocator for STL containers) for them. Things like std::vector have to allocate block of memory, but initialize it later. It's not something one should do regularly, but when you are after performance, you eventually need it.Withershins
@Jan: I do know that you do use explicit allocation and construction under the hood. I just thought that'd be outside the scope of the OP's interest. That's also why I didn't want to add the specifics of operator new replacement and allocators to this question. If you want, though, I could do it.Microclimate
@Bo: But if the constructor throws an exception, I never hit those parts of the code, do I?Microclimate
@Bo: well, yes, if you tear the example apart, you can run into trouble. But if you enclose the entire example in a try block, it should be fine, non?Microclimate
@Kerrek - I'm being picky here, but C++0x explicitly says "Initialization of the allocated object is sequenced before the value computation of the new-expression", meaning that the value assigned to p isn't available until the initialization is complete. I believe this was the intent in C++03 as well.Delative
@Bo: Please do be picky. Do you mean it should be like this: T * p = operator new(sizeof(T)); try { p = new (p) T; /*...*/ p->~T(); } catch(...){} operator delete(p);?Microclimate
@Kerrek - No. :-) The idea is that you should start out with void* temp = operator new(... and not assign that to p until you have a complete object. (Now the chat police will soon get us).Delative
@Bo: Oh OK, I see - I was sweeping the cast to T* under the carpet, which should have been a warning sign. OK, I'll edit that!Microclimate
J
2

delete is the same (ish) as free, but has important differences. The most notable difference is that delete will run an objects destructor whereas free won't.

As the comments point out, another very important detail is not mixing malloc/free and new/delete. If you allocate with malloc, use free and similarly if you use new, use delete!

Janelljanella answered 25/7, 2011 at 13:26 Comment(2)
It's a undefined behaviour to allocate with new and try to free with free() or to allocate with malloc() and try to free with delete. Some implementations will crash if you try that!Withershins
Does it work this way, new=>(malloc)=>constructor ; delete=>destructor=>(free) ? Where (malloc,free) are call "internally/automatically" . Am I rigth?Footrope
B
0

They are not the same. The delete operator in C++ calls the object's destructor, which, depending on the implementation, should release the memory. The delete function can also be overloaded, where free cannot.

Boxing answered 25/7, 2011 at 13:27 Comment(4)
It's not a function, it's an operator.Withershins
@Jan Hudec: Very true. Edited. Thanks.Boxing
@ratzip: Simply calling delete will call the default destructor for that object, releasing the memory.Boxing
@Evan: Formally wrong. It will call the destructor and release the memory. They are two separate steps and the later can be overridden per-type by defining appropriate operator delete() method (and corresponding operator new()).Withershins
M
0

malloc() and free() are not to be used in C++ code because they don't support object semantics. Furthermore, the results of calling free() to release an object that was allocated by new, or of using delete to release memory that was allocated by malloc(), are undefined. The C++ standard doesn't guarantee that the underlying implementation of operator new uses malloc(); in fact, on some implementations malloc() and new use different heaps. Check this link

delete() would delete the entire memory space occupied making it imposible to refer the varaiable once its gets deleted where in free() u could still access it.

Mahoney answered 25/7, 2011 at 13:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.