Digging deeper into the implementation of New and Delete operators, raises couple of question as listed below:
- Under what circumstances a
bad_alloc
exception is thrown? - What if there is an exception thrown inside a constructor?
- Who releases the heap memory allocated for the object, when constructor fails?
- Is it possible to reuse a chunk of memory on the heap to reconstruct the object?
- How to distinguish whether the exception is due to heap allocation error or due to a failing constructor?
Rather than trying to answer these questions individually, it's better to explore compiler implementation of " new " and " delete " operators which eventually answers the above questions.
Consider CellPhone, A Class Declaration Below
class CellPhone
{
long IMEI ;
char* DeviceName ;
public:
CellPhone(int ID, char* Name)
{
IMEI = ID ;
strcpy(DeviceName,(char*)malloc(sizeof(strlen(Name)+1)));
}
void* operator new ( size_t size );
void operator delete (void* buffer);
};
The below instruction creates an instance of this class on the heap through " new
" operator.
CellPhone* PhoneObject = new CellPhone(900,"NOKIALUMIA") ;
New Operator
The "new
" operator instantiates an object on the heap in two steps:
- Allocate the required amount of memory for the object, on heap
- Call constructor of the class to initialize the object
Instantiating the Object on Heap
To allocate memory on the heap "new
" operator uses a function " operator new " as shown in the class declaration. This function allocates memory specified by " size_t
" parameter on heap for the object and returns a " void*
" pointer. And if the memory allocation fails, run-time will throw a " bad_alloc
" exception.
void* CellPhone::operator new(size_t size) throw (const char*)
{
void* buffer = malloc(size);
if(buffer = NULL) throw "Not enough memory to allocate for the object";
return buffer ;
}
The above function can be overloaded by the developer to provide custom implementations and exemption details.
Initializing the Object by Calling Constructor
Run-time achieves this functionality through " Placement new " operator which basically receives " void*
" pointer returned by " operator new " function, allocated on the heap and calls constructor of the class to initialize the object as shown below.
Calling operator new function.
void* buffer = operator new(sizeof(CellPhone));
Typecast void* pointer to class specific pointer.
CellPhone* PhoneObject = dynamic_cast<CellPhon*>(buffer);
Constructing the object at the allocated space using " placement new " operator.
try
{
new(PhoneObject)CellPhone(900,"NOKIA LUMIA"); }
catch(exception& e)
{
cout << e.what();
operator delete( PhoneObject );
throw "Exception inside constructor";
}
void CellPhon::operator delete (void* buffer)
{
free(buffer);
buffer = NULL ;
}
The above programming structure clearly explains how the exceptions from constructor is handled. The " placement new " operator shown in instruction #2 receives pointer to the buffer allocated as shown in instruction #1 and tries to construct the object at that location.
During this process, if there is any exception thrown inside the constructor, it is caught and the allocated memory is released back to heap using " operator delete ( )
" function, which is a compliment of " operator new ( )
" function.
With the above implementation, user would clearly be able to distinguish whether the " new
" operator failed due to "heap allocation error " or due to " exception thrown " inside the constructor.
I hope this article was helpful in demystifying the obscure implementation details of "New
" and "Delete
" operators. Suggestions and comments if any are appreciated.