Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Overriding new and delete

Last updated Jan 1, 2003.

If you’re looking for more up-to-date information on this topic, please visit our C/C++ Programming article, podcast, and store pages.

For most programming tasks, the default implementation of new and delete is sufficient. In fact, many C++ programmers get along without even knowing that they can override these operators.

Yet, redefining these operators for a given class is essential in certain advanced programming tasks. One example might be when you need to ensure that all instances of a class are allocated from a custom memory pool instead of the free-store. (You can achieve something similar by using placement new, but that doesn't block users from using global new).

There are other useful applications of overriding new and delete: implementing a garbage collector, or ensuring that objects are allocated on a specific memory address (such as with a video card's internal memory buffer). In this section, I'll show how to override these operators, first on a per-class basis, and then globally.

Declaring new and delete as Class Members

Suppose you want to override new and delete for class A. Your first step is to declare these operators as member functions of this class:

#include <new> // for size_t
class A 
{
public:
 A(); 
 ~A();
 static void* operator new (size_t size); 
 static void operator delete (void *p);
};

When you declare new and delete as class members, they are implicitly declared static. Still, it's advisable to add the keyword static to their declarations to document this property explicitly.

Implementation

The implementation of new is straightforward: it calls a custom allocation function, say, allocate_from_pool(), and returns the resulting pointer. You can use any other memory allocation function that suits your needs, such as malloc() or GlobalAlloc(), as long as that function meets the memory alignment requirement of your objects. Here's a typical implementation of new:

void* A::operator new (size_t size)
{
 void *p=allocate_from_pool(size);
 return p; 
} // A's default ctor implicitly called here

Note that when this function exits, the class's default constructor executes automatically and constructs an object of type A. The matching version of delete looks as follows:

void A::operator delete (void *p)
{	
 release(p); // return memory to pool
} // A's dtor implicitly called at this point

C++ guarantees that an object's destructor is automatically called just before delete executes. Therefore, you shouldn't invoke A's destructor explicitly (doing so would cause undefined behavior as the destructor will actually run twice).

Additional Customizations

Overriding new enables you to customize its functionality in various ways. For instance, you may add an error handling mechanism to the existing implementation by defining a special exception class mem_exception. An object of this class will be thrown in case of an allocation failure:

class mem_exception {}; 
void* A::operator new (size_t size)
{
 void *p=allocate_from_pool(size);
 if (p==0)
  throw mem_exception();
 return p;
}

In a similar vein, you can extend delete's functionality to report the amount of available memory, write a message to a log file, and so on:

#include <ctime> // for time()
void A::operator delete (void *p)
{	
 release(p); 
 cout << "available pool size: " << get_pool_size();
 write_to_log("deletion occurred at: ", time(0));
}

Usage of new and delete

You use the overriding new and delete as you would use the built-in version of these operators; C++ automatically selects the overriding versions if they have been overridden in a specific class. Classes for which no overriding versions exist continue to use the built-in versions. For example

int main() 
{ 
 A *p=new A; // A::new
 delete p; // A::delete
 B *q=new B; // global built-in new
 delete q; // global built-in delete
}

Overriding new and delete Globally

Until now, I've focused on a class-based override. What if you want override new and delete not just for one class, but for the entire application? C++ allows you to do that by defining global versions of these operators.

Unlike a class-based override, the global new and delete are declared in the global namespace. (Remember that you cannot declare these operators in a different namespace.) This technique may be useful if you use an older version of Visual C++ (versions 6.0 and below) and other slightly outdated compilers. The implementation of new in Visual C++ isn't standard compliant: new returns NULL upon failure instead of throwing a std::bad_alloc exception, as required by the C++ ANSI/ISO standard. The following example overrides global new and delete to force standard compliant behavior:

#include <exception> // for std::bad_alloc
#include <new>
#include <cstdlib> // for malloc() and free()

// Visual C++ fix of operator new

void* operator new (size_t size)
{
 void *p=malloc(size); 
 if (p==0) // did malloc succeed?
  throw std::bad_alloc(); // ANSI/ISO compliant behavior
 return p;
}

The matching delete looks as follows:

void operator delete (void *p)
{
 free(p); 
}

Now you can use try and catch blocks to handle potential allocation exceptions properly, even if your compiler isn't yet fully-compliant in this regard:

int main()
{
 try
 {
 int *p=new int[10000000]; // user-defined new
 //..use p
 delete p; // user-defined delete
 }
 catch (std::bad_alloc & x)
 {
 std::cout << x.what(); // display exception
 }
}

Very few programming languages allow you to access and modify their inner-workings as does C++. Indeed, overriding new and delete is a very powerful feature: it gives you tighter control over the language's memory management policy and enables you to extend its functionality in various ways.

However, the built-in implementation of new and delete is suitable for most purposes. Don't override these operators unless you have compelling reasons to do so, and when doing so, remember always to override both of them.