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?
1. To take charge or control over how to allocate memory
2. To aid in debugging; keep track of memory allocation and deallocation in the program
3. To do some other operation apart from allocating memory at the time of memory allocation/deallocation
Given below is a simple sample demonstrating overloaded new and delete:
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?
1. To take charge or control over how to allocate memory
2. To aid in debugging; keep track of memory allocation and deallocation in the program
3. To do some other operation apart from allocating memory at the time of memory allocation/deallocation
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:
1. The overloaded new operator receives a parameter num of type size_t. This is the number of bytes of memory to be allocated. The compiler calculates and sends this to us!
2. The return type of the overloaded new must be void*. It is expected to return a pointer to the beginning of the block of memory allocated. Note that after our overloaded new returns, the compiler then automatically calls the constructor also as applicable.
3. The overloaded delete operator receives a parameter ptr of type void*. This is the pointer the user is trying to delete.
4. The overloaded delete operator should not return anything.
5. In this sample implementation, since the focus is only on showing how to overload, we have done the memory allocation and deallocation using malloc() and free() 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:
1. The overloaded new operator receives a parameter num of type size_t. This is the number of bytes of memory to be allocated. The compiler calculates and sends this to us!
2. The return type of the overloaded new must be void*. It is expected to return a pointer to the beginning of the block of memory allocated. Note that after our overloaded new returns, the compiler then automatically calls the constructor also as applicable.
3. The overloaded delete operator receives a parameter ptr of type void*. This is the pointer the user is trying to delete.
4. The overloaded delete operator should not return anything.
5. In this sample implementation, since the focus is only on showing how to overload, we have done the memory allocation and deallocation using malloc() and free() 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:
1. This time, the overloaded new takes in an additional parameter – x, of type char.
2. Such an overloaded new is called when invoked with an argument (‘*‘ in main, of type char).
3. 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.
4. Such a new operator (that takes arguments) is called the placement new operator.
5. Like this, we can have any number of new operators with varying number of arguments and types
6. 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:
1. This time, the overloaded new takes in an additional parameter – x, of type char.
2. Such an overloaded new is called when invoked with an argument (‘*‘ in main, of type char).
3. 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.
4. Such a new operator (that takes arguments) is called the placement new operator.
5. Like this, we can have any number of new operators with varying number of arguments and types
6. 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 its 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:
1. Creating a node dynamically (thanks to new)
2. Initializing that node to the value 10 (thanks to a constructor in Node class)
3. 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.
Source:
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 its 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:
1. Creating a node dynamically (thanks to new)
2. Initializing that node to the value 10 (thanks to a constructor in Node class)
3. 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.
Source:

0 comments:
Post a Comment