The new
and delete
operators can also be overloaded like other operators in C++. There are interesting possibilities of doing the same. However, some care should be taken regarding the parameters to accept, value to return and place to declare. This article covers all possible forms of overloading these operators and their uses.
In C++, the new
operator is the recommended way of allocating memory, rather than using the malloc()
of C, which is nevertheless available. The reason for preferring new
over malloc
is that new
will also result in the constructor (wherever applicable) being called, but malloc
does not. This is a very big point in C++; so big that I will devote an entire article for this later on. But for now, let us see why and how to overload new
.
Why overload new and delete?
- To take charge or control over how to allocate memory
- To aid in debugging; keep track of memory allocation and deallocation in the program
- To do some other operation apart from allocating memory at the time of memory allocation/decallocation
Given below is a simple sample demonstrating overloaded new
and delete
:
void* operator new(size_t num) { return malloc(num); } void operator delete(void *ptr) { free(ptr); }
Observe the following:
- The overloaded
new
operator receives a parameternum
of typesize_t
. This is the number of bytes of memory to be allocated. The compiler calculates and sends this to us! - The return type of the overloaded
new
must bevoid*
. It is expected to return a pointer to the beginning of the block of memory allocated. Note that after our overloadednew
returns, the compiler then automatically calls the constructor also as applicable. - The overloaded
delete
operator receives a parameterptr
of typevoid*
. This is the pointer the user is trying to delete. - The overloaded
delete
operator should not return anything. - In this sample implementation, since the focus is only on showing how to overload, we have done the memory allocation and deallocation using
malloc()
andfree()
functions. In real life situations, we would prefer to do something more than this!
Deleting an array of objects is not the same as deleting an object, i.e. delete[] ptr;
and delete ptr;
are totally different and involve different operators! In case you are interested in overloading delete
for an array, use the following:
void operator delete[](void *ptr) { free(ptr); }
The overloaded new
operator can accept additional arguments as required! Yes, it is possible to have multiple overloaded new
operators in the same program! Consider the following example:
void* operator new(size_t num, char x) { void *ptr; if (ptr = malloc(num)) *ptr = x; return ptr; } int main() { char *p = new('*') char; }
Observe the following:
- This time, the overloaded
new
takes in an additional parameter –x
, of typechar
. - Such an overloaded
new
is called when invoked with an argument (‘*
‘ inmain
, of typechar
). - Note that there need not be any connection between the type for which memory is being allocated and the type of the parameter in
new
. - Such a
new
operator (that takes arguments) is called the placement new operator. - Like this, we can have any number of
new
operators with varying number of arguments and types - The code inside is left to the programmer. In our example, we have initialized the character for which memory was allocated, by the parameter.
Thus, while we can have any number of overloaded new
operators, we can have only a single overloaded delete
operator. Do remember that there are 2 forms of delete
though! And also do remember that it is a good programming habit to overload both new
and delete
, or none of them; which means I would consider it bad if you overload new
without overloading delete
or vice-versa.
So far we have been considering overloading new
and delete
in the global scope. It is also possible for us to overload new
and delete
within classes. This would restrict their scope and operations to that class. Perhaps a simple illustration will throw more light:
class A { public: void* operator new(size_t num) { //... } }; class B { public: void* operator new(size_t num) { //... } }; class C { }; int main() { A *a = new A(); // Will call A's new operator B *b = new B(); // Will call B's new operator C *c = new C(); // Will call the global new operator char *ptr = new char; //... }
As can be seen from the program, when the new
operator is used to create an instance of A
dynamically, the overloaded new
operator of class A
is used. The same rule holds good for class B
. However, in the case of instantiating class C
, the global new
is used since there is no overloaded new
operator in that class. The same rules hold good for the delete
operator too. Thus, new
and delete
overloads can be present on a per-class basis.
Within these “local” new
and delete
overloads, if you wish to call the global new
to allocate memory, you can continue to use the scope resolution operator (::
) like this: ::new <whatever>
.
To conclude this article, consider a very practical use of overloaded placement new operator within a class. Consider a class called LinkedList
that implements a linked list of nodes, where each node is represented by an object of class Node
. Let us say we want to create a node with it’s data as 10
, and append it to the LinkedList
object list1
. Consider the possibility below:
Node *nodePtr = new(list1) Node(10);
At one go, we are achieving the following:
- Creating a node dynamically (thanks to new)
- Initializing that node to the value 10 (thanks to a constructor in Node class)
- Appending the new node at the end of the linked list
list1
(thanks to placement new)
Of course, the Node
constructor and placement new
in LinkedList
class needs to be defined by the programmer.
Hope I was able to stress on the advantages of overloading new
and delete
through this article, and make it very clear to you as to how to achieve this overloading.
Related Articles
1 user responded in this post