What/how does C++ delete operator work - c++

When we call delete on a pointer it calls the destructor of the object, but after the destructor it frees the memory the object was pointing to. If we overwrite the delete operator in a class, how would we do the same thing the original delete operator does with actually freeing the memory?
I'm thinking in terms of garbage collecting and smart pointers. Is it possible to have the default delete functionality of actually freeing the memory somehow (copy/paste the default delete operator code) if we overwrite a classes delete operator?
Yes, I'm just playing around with bastardizing C++. This isn't really for anything specific. Just trying to understand the possibilities around this.
I hate templated smart pointers and would love to try and implement smart pointers that allow you to still use the normal delete operator and no templated classes to wrap the pointer into.

How the delete operator frees memory depends on how the
corresponding new operator acquired it. If you're implementing
class specific operators, you probably need to implement both
(or neither). In this case, you can use ::operator new to get
the memory, and ::operator delete to free it.
If you're thinking of replacing the global operator new and
operator delete, you can use malloc or free to allocate.
Formally, these are the only functions guaranteed not to use
new and delete themselves (but it's a good bet that
functions like memset don't either).
If you're interested in garbage collection, of course, operator
new will allocate from the garbage collected arena, and
operator delete will be a no-op. (I've done this using the
Boehm collector, and it works quite well.) You can do this on
a class by class basis, or you can do it globally. If you do
this, you do not have to delete except if your program logic
requires it. But be aware that the garbage collector will not
call your destructor. If the destructor is trivial, or is only
used for memory management (and thus has been subsumed by the
garbage collector), this is fine, and this is often the case.
One thing that you cannot change is that the new operator will
call a constructor (assuming the type has a non-trivial one),
after having called operator new, and the delete operator
will call the destructor, before calling operator delete.

Related

Realloc and glocal new/delete operator overriding

Stated that there is no C++ equivalent of the C realloc function, I've found in another question that such a thing is automatically managed by std::vector and we should use it instead.
I'm fine with it. I guess that, since there is no other way of do reallocation, the std::vector will just call realloc for me.
However, the question is: if I'm overriding the new and the delete operators for managing of tracking memory usage globally, they will not be called in the case someone calls old C functions (malloc, calloc, realloc, free).
How can it be done? Is it correct that std::vector replaces realloc?
std::vector won't call realloc; it will use its allocator to achieve something similar: allocating new memory, moving objects into it, then deallocating the old memory. The default allocator uses operator new and operator delete, and so will use your replacements if you provide them.
realloc would be entirely the wrong thing to do if the vector contains non-trivial objects; it copies raw data, while C++ objects generally have to be copied or moved by calling their special functions.
Nothing in the C++ library (except perhaps the default implementations of operator new and operator delete) will call the C allocation functions directly. Since you shouldn't be calling them yourself, you only need to worry about them if you're using a C library.

Why use malloc/free, when we have new/delete?

What is the use of malloc and free when we have new and delete in C++. I guess function of both free and delete is same.
They're not the same. new calls the constructor, malloc just allocates the memory.
Also, it's undefined behavior mixing the two (i.e. using new with free and malloc with delete).
In C++, you're supposed to use new and delete, malloc and free are there for compatibility reasons with C.
In C++, it is rarely useful that one would use malloc & free instead of new& delete.
One Scenario I can think of is:
If you do not want to get your memory initialized by implicit constructor calls, and just need an assured memory allocation for placement new then it is perfectly fine to use malloc and free instead of new and delete.
On the other hand, it is important to know that mallocand new are not same!
Two important differences straight up are:
new guarantees callng of constructors of your class for initializing the class members while mallocdoes not, One would have to do an additional memset or related function calls post an malloc to initialize the allocated memory to do something meaningful.
A big advantage is that for new you do not need to check for NULL after every allocation, just enclosing exception handlers will do the job saving you redundant error checking unlike malloc.
First, when you speak of new and delete, I assume you mean the
expressions, and not the operator new and operator delete functions.
The new and delete expressions are not related to malloc and
free, and only manage memory incidentally; their main role is to
manage object lifetime: a new expression will call the operator new
function to obtain memory, and then call the constructor; a delete
expression will call the destructor before calling operator delete to
free the memory. For the most part, objects should be created, and
not simply allocated, which means using the expressions exclusively.
There are some rare cases where one wants to separate allocation and
initialization (creation); implementing things like std::vector is a
classical example, where you'll allocate for many objects in one go, but
only construct one at a time. In such cases, you'll use the operator
new function for allocation, and placement new for initialization; at
the other end, you'll explicitly call the constructor (something like
p->~T()) for destruction, and use the operator delete function to
free the memory.
Off hand, I can only think of two cases where you'd use malloc and
free in C++. The first is to implement your own replacements of the
::operator new and ::operator delete functions. (I often replace
the global ::operator new and ::operator delete with debugging
versions, which trace allocations, put guard zones around the allocated
memory, etc.) The other is when interacting with a legacy library
written in C: if the library says to pass a pointer to memory allocated
by malloc (because it will free it itself using free), or more
commonly, returns a pointer to memory allocated by malloc, which
you're expected to free, then you must use malloc and free. (The
better libraries will provide their own allocation and deallocation
functions, which do more or less what the new and delete operators
do, but there will always be things like strdup().)

How to properly free the memory allocated by placement new?

I've been reading somewere that when you use placement new then you have to call the destructor manually.
Consider the folowing code:
// Allocate memory ourself
char* pMemory = new char[ sizeof(MyClass)];
// Construct the object ourself
MyClass* pMyClass = new( pMemory ) MyClass();
// The destruction of object is our duty.
pMyClass->~MyClass();
As far as I know operator delete normally calls the destructor and then deallocates the memory, right? So why don't we use delete instead?
delete pMyClass; //what's wrong with that?
in the first case we are forced to set pMyClass to nullptr after we call destructor like this:
pMyClass->~MyClass();
pMyClass = nullptr; // is that correct?
BUT the destructor did NOT deallocate memory, right?
So would that be a memory leak?
I'm confused, can you explain that?
Using the new expression does two things, it calls the function operator new which allocates memory, and then it uses placement new, to create the object in that memory. The delete expression calls the object's destructor, and then calls operator delete. Yeah, the names are confusing.
//normal version calls these two functions
MyClass* pMemory = new MyClass; void* pMemory = operator new(sizeof(MyClass));
MyClass* pMyClass = new( pMemory ) MyClass();
//normal version calls these two functions
delete pMemory; pMyClass->~MyClass();
operator delete(pMemory);
Since in your case, you used placement new manually, you also need to call the destructor manually. Since you allocated the memory manually, you need to release it manually.
However, placement new is designed to work with internal buffers as well (and other scenarios), where the buffers were not allocated with operator new, which is why you shouldn't call operator delete on them.
#include <type_traits>
struct buffer_struct {
std::aligned_storage_t<sizeof(MyClass), alignof(MyClass)> buffer;
};
int main() {
buffer_struct a;
MyClass* pMyClass = new (&a.buffer) MyClass(); //created inside buffer_struct a
//stuff
pMyClass->~MyClass(); //can't use delete, because there's no `new`.
return 0;
}
The purpose of the buffer_struct class is to create and destroy the storage in whatever way, while main takes care of the construction/destruction of MyClass, note how the two are (almost*) completely separate from each other.
*we have to be sure the storage has to be big enough
One reason this is wrong:
delete pMyClass;
is that you must delete pMemory with delete[] since it is an array:
delete[] pMemory;
You can't do both of the above.
Similarly, you might ask why you can't use malloc() to allocate memory, placement new to construct an object, and then delete to delete and free the memory. The reason is that you must match malloc() and free(), not malloc() and delete.
In the real world, placement new and explicit destructor calls are almost never used. They might be used internally by the Standard Library implementation (or for other systems-level programming as noted in the comments), but normal programmers don't use them. I have never used such tricks for production code in many years of doing C++.
You need to distinguish between the delete operator and operator delete. In particular, if you're using placement new, you explicitly invoke the destructor and then call operator delete (and not the delete operator) to release the memory, i.e.
X *x = static_cast<X*>(::operator new(sizeof(X)));
new(x) X;
x->~X();
::operator delete(x);
Note that this uses operator delete, which is lower-level than the delete operator and doesn't worry about destructors (it's essentially a bit like free). Compare this to the delete operator, which internally does the equivalent of invoking the destructor and calling operator delete.
It's worth noting that you don't have to use ::operator new and ::operator delete to allocate and deallocate your buffer - as far as placement new is concerned, it doesn't matter how the buffer comes into being / gets destroyed. The main point is to separate the concerns of memory allocation and object lifetime.
Incidentally, a possible application of this would be in something like a game, where you might want to allocate a large block of memory up-front in order to carefully manage your memory usage. You'd then construct objects in the memory you've already acquired.
Another possible use would be in an optimized small, fixed-size object allocator.
It's probably easier to understand if you imagine constructing several MyClass objects within one block of memory.
In that case, it would go something like:
Allocate a giant block of memory using new char[10*sizeof(MyClass)] or malloc(10*sizeof(MyClass))
Use placement new to construct ten MyClass objects within that memory.
Do something.
Call the destructor of each of your objects
Deallocate the big block of memory using delete[] or free().
This is the sort of thing you might do if you're writing a compiler, or an OS, etc.
In this case, I hope it's clear why you need separate "destructor" and "delete" steps, because there's no reason you will call delete. However, you should deallocate the memory however you would normally do it (free, delete, do nothing for a giant static array, exit normally if the array is part of another object, etc, etc), and if you don't it'll be leaked.
Also note as Greg said, in this case, you can't use delete, because you allocated the array with new[] so you'd need to use delete[].
Also note that you need to assume you haven't overridden delete for MyClass, else it will do something totally different which is almost certainly incompatible with "new".
So I think you're unlikley to want to call "delete" as you describe, but could it ever work? I think this is basically the same question as "I have two unrelated types that don't have destructors. Can I new a pointer to one type, then delete that memory through a pointer to another type?" In other words, "does my compiler have one big list of all allocated stuff, or can it do different things for different types".
I'm afraid I'm not sure. Reading the spec it says:
5.3.5 ... If the static type of the operand [of the delete operator] is different from its dynamic type, the static type shall be a base
class of the operand's dynamic type and the static type shall have a
virtual destructor or the behaviour is undefined.
I think that means "If you use two unrelated types, it doesn't work (it's ok to delete a class object polymorphically through a virtual destructor)."
So no, don't do that. I suspect it may often work in practice, if the compiler does look solely at the address and not the type (and neither type is a multiple-inheritance class, which would mangle the address), but don't try it.
you'll be using placement new for shared memory IPC: one "initializer" process reserves and maps the shared memory and then the mapped memory is shared by all processes

Which operator delete?

Is there a difference between:
operator delete(some_pointer);
and
delete some_pointer;
and if so what is the difference and where one should use one and where the other version of this operator?
Thanks.
Ironically, the delete operator and operator delete() are not the same thing.
delete some_pointer; calls the destructor of the object pointed to by some_pointer, and then calls operator delete() to free the memory.
You do not normally call operator delete() directly, because if you do, the object's destructor will not be called, and you are likely to end up with memory leaks.
The only time you have to care about operator delete() is when you want to do your own memory management by overriding operator new() and operator delete().
To top it off, you should also be aware that delete and delete [] are two different things.
operator delete() simply frees the memory. delete some_pointer calls some_pointer's destructor, and then calls operator delete().
delete some_pointer; is the "correct" one to use.
operator delete(some_Pointer); exist mainly as an artifact of the syntax for defining you own delete operator. That is, because you define an plus operator as;
myclass::operator+(myclass b) {....}
you really could write:
myclass c = a.operator+(b);
but no one ever does that. They use:
myclass c = a + b;
Similarly, you could write operator delete(some_Pointer);, but no one ever does.
At least in my experience, it's more common to implement operator new and operator delete than to actually use (i.e., call) them, at least directly.
Usually, you use operator new and operator delete indirectly -- you write a new expression, like A *a = new A;. To implement this, the compiler generates code that invokes operator new to allocate raw memory, then invokes a A::A to convert that raw memory into an A object, much as if you'd written:
void *temp = operator new(sizeof A); // allocate raw memory with operator new
A *a = new(temp) A; // convert raw memory to object with placement new
When you're done with the object, you use delete A;. To implement that, the compiler invokes the dtor for the object, and then frees the memory, roughly like you'd done:
a->~A();
operator delete(a);
There are also operator [] new and operator [] delete, which are used when/if you allocate/delete arrays -- but there isn't necessarily any real difference between the normal version and the array version -- they both just allocate a specified amount of raw memory (though you might guess that the array versions will allocate relatively large amounts of memory, and do some optimization on that basis).
In any case, if you want to optimize how memory is allocated for objects of a particular class you overload these to do it. There are a fair number of existing implementations that you can drop-in and use, especially for situations where you expect to allocate a large number of tiny objects so you need to minimize the overhead associated with each allocation (e.g., HeapLayers, Loki's small block allocator).
One interesting little tidbit: operator new, operator [] new, operator delete and operator [] deleteare alwaysstaticclass members, even if you don't explicitly includestatic` in their declaration/definition.
There are also global versions of all four (::operator new, ::operator [] new, ::operator delete and ::operator [] delete). These mark the "border" between the "internal" C++ memory management, and the outside world. Typically they allocate relatively large chunks of memory from the operating system, and then return smaller pieces to the rest of the program upon request. If you want to (try to) optimize memory management for your entire program, you typically do it by overloading (or, really, replacing) these. Again, the typical reason would be if you expect to allocate a lot of small objects (but not in just a few classes). One example of this is the Boost Pool library.
Direct use of any of the above is generally restricted to situations where you need a block of raw memory, not objects. One example would be implementing your own container classes. For example, std::vector normally uses ::operator new (via an Allocator object) to allocate memory in which to store objects. Since it needs to be able to allocate storage, but only later (or perhaps never) create objects in that storage, it can't just use something like data = new T[size]; -- it has to allocate raw memory, then use placement new to create objects in the memory as you add them to the collection (e.g., when you push_back an object). The same is true with std::deque. If you wanted (for example) to implement your own circular buffer "from the ground up", handling all the memory management directly instead of using something like vector for storage, you'd probably need/want to do the same.

About constructors/destructors and new/delete operators in C++ for custom objects

Suppose I have a Linked List I created myself. It has its own destructor, which frees the memory. This Linked List does not overload new or delete.
Now, I'm trying to create an array of said linked lists (open hashing, if I understand correctly). Then I allocate the necessary memory inside the constructor of this open hashing class. The new operator being called inside the constructor is enough to correctly allocate the memory for the array, right? I'm not sure because I haven't overloaded new for the Linked List class.
Also, assuming my array of Linked Lists is called elements, could I just write "delete[] elements" in the destructor? Would that call the destructor for each element in the array and correctly free the memory?
Finally, if both my assumptions are correct (ie, I don't have to overload new and delete to use them with my custom class), what is the point of overloading such operators?
Yeah you are right. A plain
elements = new LinkedList[N];
is enough to allocate them. You can then access them
elements[i]->push(....);
and delete them in your destructor using the way you showed:
delete[] elements;
The compiler will remember how many elements were allocated, and call the destructor for each list correctly. The point of overloading the new and delete operator is to provide custom memory allocation strategy. For example, you could preallocate memory, and then take from that pool, instead of allocating everytime again memory from the OS.
But note, you have to write a copy constructor and copy assignment operator too. Since if someone copies your hash map, the linked list has to be copied too, and not just the pointer. Or you can make the copy constructor and copy assignment operator private and don't define them, disallowing copies of your hash map:
....
private:
MyHashMap(MyHashMap const& rhs);
MyHashMap & operator=(MyHashMap const& rhs);
....
The new operator does two things: allocating memory and calling the constructor.
The delete operator calls the destructor and then frees the memory.
Arrays created with new [] must be destroyed with delete[].
You generally don't need to overload new or delete except for performance reasons. You might have a predictable pattern of allocation/deallocation which makes a particular allocation strategy very suitable (fast or low memory use).
You may wish to have a look at this page.
All of your assumptions are correct.
There are lots of uses for overloading new and delete, but it is not done often. One common reason is for tracking memory allocations in order to spot memory leaks. A lot of compile time leak trackers do this, but have sort of become obsolete with better external apps like valgrind. You can also do things like use pooled memory.