Operators new and delete

Sadly, there are segments of the C++ programming community that seem to equate failure to use the more complex data types of C++ with a lack of programming sophistication, which results in overkill rather than adherence to a prime mantra: Use a solution that is as simple as possible and no simpler."

From the book Efficient C++ by Dov Bulka and David Mayhew


new and delete

We'll use this Foo class for the examples.
class Foo
{
public:
  Foo(int a, int b, int c) : a_(a), b_(b), c_(c) { 
    std::cout << "In Foo constructor\n";
  }

  ~Foo() {
    std::cout << "In Foo destructor\n";
  }

  friend std::ostream &operator<<(std::ostream& os, const Foo& foo);

private:
  int a_, b_, c_;
};

std::ostream &operator<<(std::ostream& os, const Foo& foo)
{
  return os << "(" << foo.a_ << "," << foo.b_ << "," << foo.c_ << ")";
}
Given the program below, what is printed?
int main(void)
{
  Foo *pf1 = new Foo(1, 2, 3);
  delete pf1;
  return 0;
}
As an aside, the new expression and the delete expression (built into the language) are in the global namespace, so we can qualify them using the scope resolution operator:
int main(void)
{
  Foo *pf1 = ::new Foo(1, 2, 3); // invoke global new
  ::delete pf1;                  // invoke global delete
  return 0;
}
This is a trivial program and we all know what is printed. But, what is really happening at runtime? To understand, you have to know the difference between the new expression and operator new.

The new expression does double duty. Informally, it's something like this:

  1. Find available memory,
  2. Make sure any constructor(s) get called to initialize the memory;
These two tasks are very different and quite unrelated, although you may have never realized it before. So, when the compiler sees this:
Foo *pf1 = ::new Foo(1, 2, 3);
it generates code that is somewhat equivalent to this:
void *buffer = ::operator new(sizeof(Foo)); // allocate the proper space using operator new
Foo::Foo(buffer, 1, 2, 3);                  // call the constructor using buffer as this
Foo *pf1 = static_cast<Foo *>(buffer);      // assign the buffer to the pointer
Of course, the syntax for calling a constructor is not quite like that, since you can't call a constructor directly in C++ source code. The concept to understand is that the constructor is told exactly where it's data has been allocated so that it can initialize the data (via offsets from this).


MSVC++ 6.0 defines operator new like this:

void * operator new( unsigned int cb )
{
  void *res = _nh_malloc( cb, 1 );
  return res;
}
This makes sense, since all operator new needs to know is the size (count of bytes) to allocate. That's essentially all it does. Sorry, no magic here.

Essentially, when the compiler sees something like this (built-in types):
char *p1 = ::new char[50];
it is translated to something like this:
char *p1 = ::operator new(50);
or more accurately:
char *p1 = static_cast<char *>(::operator new(50));
If you step through this code:
Foo *pf1 = ::new Foo(1, 2, 3);
you will first find yourself in the code for operator new, then you will end up in the constructor.

Likewise, when the compiler sees this:

::delete pf1;
it generates code to do something like this:
pf1->~Foo();           // call the destructor
operator delete(pf1);  // free the memory that was allocated
MSVC++ 6.0 defines operator delete like this:
void __cdecl operator delete(void *p) _THROW0()
{ 
  free(p); // free an allocated object
}
which is also as simple as the corresponding operator new.


Essentially, using the new expression with built-in types is similar to using operator new:

char *buffer1 = ::new char[12];      // allocate 12 bytes (returns char *)
void *buffer2 = ::operator new(12);  // allocate 12 bytes (returns void *)

::delete [] buffer1;         // free memory allocated by ::new
::operator delete(buffer2);  // free memory allocated by ::operator new
Microsoft's implementations

So, it is the constructor and destructor that print the messages in the above program, (which we knew by just looking at the code.)

int main(void)
{
  Foo *pf1 = ::new Foo(1, 2, 3); // constructor prints the message
  ::delete pf1;                  // destructor prints the message
}
Later, we'll see how to "intercept" the call to operator new and operator delete (think overload) and take full control of allocation and deallocation (think custom memory management).

Compare the details for creating an object on the stack and on the heap:

Creating a Foo object on the stack:

int main(void)
{
  Foo f(1, 2, 3);   // create Foo on the stack
  return 0;
}
Assembly details for stack creation.

Creating a Foo object on the heap:

int main(void)
{
  Foo *pf1 = ::new Foo(1, 2, 3); // allocate memory, construct
  ::delete pf1;                  // destroy, deallocate memory
}
Assembly details for heap creation.

Self-check: Step through the above code in a debugger to see which functions are being called and in what order (operator new, malloc, operator delete, constructors, destructors, etc.)


Placement new

Suppose we already have some memory (just lying around, of course) that we want to re-use for our Foo object (instead of allocating another chunk). We can "pass" another parameter in the new expression to indicate our intention. The syntax for this technique:
new (address_to_place_object) type
This is only slightly more complex than our "normal" invocation of new:
new type
The implementation for placement new is trivial and looks something like this:
void *operator new(size_t, void *address)
{
  return address;
}
The actual code in MSVC++ 6.0 looks like this:
#ifndef __PLACEMENT_NEW_INLINE
#define __PLACEMENT_NEW_INLINE
inline void *__cdecl operator new(size_t, void *_P)
{
  return (_P); 
}
#endif
In essence, by using placement new, what we are telling the new expression is "Don't worry about allocating the memory, I've already done it for you. Just construct the object at the address I tell you."

To create a Foo object in pre-allocated memory:

int main(void)
{
  int size = sizeof(Foo);                 // 12 bytes (3 integers, a_, b_, c_)
  char *buffer = ::new char[size];        // allocate 12 bytes on the heap
  
  Foo *pf2 = ::new (buffer) Foo(1, 2, 3); // construct a Foo object in buffer (placement new)
  
  ::delete [] buffer;  // delete char array (not pf2!!) when were done

  return 0;
}
Now, what is printed by the program above that uses placement new? Notice a problem?

Actually, we need to handle a slight detail ourselves:

int main(void)
{
  char *buffer = ::new char[sizeof(Foo)];  // allocate 12 bytes on the heap
  Foo *pf2 = ::new (buffer) Foo(1, 2, 3);  // construct a Foo object in buffer (placement)

  pf2->~Foo();         // destruct the Foo object that's in buffer
  ::delete [] buffer;  // delete char array (not pf2!!) when were done

  return 0;
}
Assembly details for placement new

As a simple experiment, consider the two loops below. By looping 10,000,000 times, what kind of performance difference (if any) would you expect to see?

  int it = 10000000;
Loop #1
    // Using the "normal" new
  for (int i = 0; i < it; i++)
  {
    Foo *pf2 = ::new Foo(i, i, i);  // construct a Foo object (allocate/initialize)
      // ... do something useful ...
    ::delete pf2;                   // destruct the Foo object (deallocate/destructor)
  }
Loop #2
    // Using placement new
  char *buffer = ::new char[sizeof(Foo)];   // allocate 12 bytes on the heap
  for (int i = 0; i < it; i++)
  {
    Foo *pf2 = ::new (buffer) Foo(i, i, i); // construct a Foo object in buffer
      // ... do something useful ...
    pf2->~Foo();                            // destruct the Foo object that's in buffer
  }
  ::delete [] buffer;  // delete char array (not pf2!!) when were done
We've managed to move the dynamic allocation for the objects outside the loop. Can we remove the dynamic allocation and still use placement new?

Now you've got control over how/when objects get constructed:

Important Note: When using placement new, make sure you call the object's destructor and that you call delete on the proper object.


Arrays: new[ ] and delete[ ]

There are versions of new and delete that are used for arrays. (They behave quite differently than the non-array versions.) Unfortunately, the compilers don't tell you when you are doing something dangerous (like mixing them together).

Given our Foo class above, will this compile?

Foo *pf = ::new Foo[5];
For the sake of demonstration, let's modify the constructor:
Foo(int a = 0, int b = 0, int c = 0) : a_(a), b_(b), c_(c) { 
  std::cout << "In Foo constructor\n";
}
Now, what would you expect to see from this program?
Foo *pf = ::new Foo[5];
::delete [] pf;
The code that the compiler sees for this:
Foo *pf = ::new Foo[5];
is somewhat like this:
  // allocate the proper space for 5 Foo objects
void *buffer = ::operator new(5 * sizeof(Foo)); 

  // construct each object at the correct memory location
for (int i = 0; i < 5; i++)
{
    // calculate offset of next object to construct
  void *location = static_cast<char *>(buffer) + (i * sizeof(Foo));

    // call the constructor using value of location as this
  Foo::Foo(location);    
}

  // assign the fully constructed memory to the pointer
Foo *pf = static_cast<Foo *>(buffer);    
And this code:
::delete [] pf;
is seen as something like this:
  // deconstruct each object at the correct memory location
for (int i = 0; i < 5; i++)
  (pf + i)->~Foo(); 

::operator delete(pf1);  // free the memory that was allocated
Again, realize that the above code is only pseudo-code using C++ syntax and semantics. You can't code this way and expect it to work correctly.

BTW, this is "old-style" C++:

Foo *pf = ::new Foo[5];
::delete [5] pf; // anachronistic use of array size in vector delete
Given your "new" knowledge of new[] and delete[], what might you expect to be printed from this program?
Foo *pf = ::new Foo[5]; // operator new[]
::delete pf;            // operator delete
How about this?
Foo *pf = ::new Foo; // operator new
::delete [] pf;      // operator delete[]

Results

Assembly details for new and new[]
Assembly details for delete and delete[]


Let's go back to our original (non-default) constructor (that prevents us from creating an array of objects):

Foo(int a, int b, int c) : a_(a), b_(b), c_(c) { 
  std::cout << "In Foo constructor\n";
}
Now, how would you create an array of 5 Foo objects?
int count = 5;

  // allocate space for 5 Foo objects
void *buffer = ::new char[count * sizeof(Foo)];  

  // point to the raw (uninitialized) memory
Foo *pf = static_cast<Foo *>(buffer);

  // construct Foo objects in place
for (int i = 0; i < count; i++)
  ::new (pf + i) Foo(i, i, i);

  // ... do something with the objects

  // destroy the objects manually (in reverse order)
for (int j = count - 1; j >= 0; j--)
  (pf + j)->~Foo();

  // free the memory 
::delete [] buffer;
Note you could use array notation as well and let the compiler do the pointer arithmetic:
  // construct Foo objects in place
for (int i = 0; i < count; i++)
  ::new (&pf[i]) Foo(i, i, i);

  // destroy the objects manually (in reverse order)
for (int j = count - 1; j >= 0; j--)
  pf[j].~Foo();

Self-check: Step through the above code in a debugger to follow the code path at runtime and see that you understand exactly what is happening "behind the scenes."


Overloading Operators new and delete

Since operator new and operator delete are operators (not unlike +, -, %, etc), you can overload them in your classes.

Our Foo class also uses std::cout to show when they are being called in the examples. These implementations are obviously trivial. We're just "wrapping" the global ::operator new and ::operator delete:

static void *operator new(size_t size) {
  std::cout << "In Foo::operator new, size = " << size << "\n";
  return ::operator new(size);
}

static void operator delete(void *memory) {
  std::cout << "In Foo::operator delete\n";
  ::operator delete(memory);
}
Now, given this program:
int main(void)
{
  Foo *pf1 = new Foo(1, 2, 3);    // allocates memory using Foo::operator new
  delete pf1;                     // deallocates memory using Foo::operator delete

  std::cout << std::endl;

  Foo *pf2 = ::new Foo(4, 5, 6);  // allocates memory using global ::operator new
  ::delete pf2;                   // deallocates memory using global ::operator delete

  return 0;
}
The output is:
In Foo::operator new, size = 12
In Foo constructor: 1,2,3
In Foo destructor: 1,2,3
In Foo::operator delete

In Foo constructor: 4,5,6
In Foo destructor: 4,5,6
Exciting. Truly exciting.

Note that operator delete can have two parameters. The second parameter will be set to the size of the object (automatically by the compiler) being deleted:

static void operator delete(void *memory, size_t size) {
  std::cout << "In Foo::operator delete, size = " << size << "\n";
  ::operator delete(memory);
}
Using the above overloaded operator delete, our output would look like this:
In Foo::operator new, size = 12
In Foo constructor: 1,2,3
In Foo destructor: 1,2,3
In Foo::operator delete, size = 12

In Foo constructor: 4,5,6
In Foo destructor: 4,5,6
The purpose of this parameter is for derived classes in which the operator delete might be inherited. (The size of the derived class may not be the same as the base class size.)

Unfortunately, I get a warning from MSVC++ 6.0 with this syntax, although other compilers compile it just fine (including MSC++ 7.1). You should try this with your compilers to see how they fare.

We can also overload the array versions as well:

static void *operator new[](size_t size) {
  std::cout << "In Foo::operator[] new, size = " << size << "\n";
  return ::operator new[](size);
}

static void operator delete[](void *memory) {
  std::cout << "In Foo::operator delete[]\n";
  ::operator delete[](memory);
}
Now this code:
Foo *pf3 = new Foo[3]; // allocates memory using Foo::operator new[]
delete [] pf3;         // deallocates memory using Foo::operator delete[]
prints this:
In Foo::operator[] new, size = 40
In Foo constructor: 0,0,0
In Foo constructor: 0,0,0
In Foo constructor: 0,0,0
In Foo destructor: 0,0,0
In Foo destructor: 0,0,0
In Foo destructor: 0,0,0
In Foo::operator delete[]

Here's a more real-world use of overloaded operator new and operator delete using our Foo class above:

operator new:

static void *operator new(size_t size) {
    // If we're trying to construct a non-Foo object, just
    // use the global ::operator new
  if (size != sizeof(Foo))
    return ::operator new(size);

  Foo *pf = freelist_;
  if (freelist_)
    freelist_ = freelist_->next_;
  else
  {
    Foo *newpage = static_cast<Foo *>(::operator new(FOOS_PER_PAGE * sizeof(Foo)));
    for (int i = 0; i < FOOS_PER_PAGE - 1; ++i)
      newpage[i].next_ = &newpage[i + 1];

    newpage[FOOS_PER_PAGE - 1].next_ = 0;
    pf = newpage;
    freelist_ = &newpage[1];
  }
  return pf;
}
operator delete:
static void operator delete(void *object) {
  Foo *pf = static_cast<Foo *>(object);
  pf->next_ = freelist_;
  freelist_ = pf;
}
We have to modify our Foo class slightly to handle this new implementation:

(In the class declaration):

private:
  int a_, b_, c_;
  Foo *next_;
  static const int FOOS_PER_PAGE;
  static Foo *freelist_;
};
(In an implementation file):
Foo *Foo::freelist_ = 0;
const int Foo::FOOS_PER_PAGE = 16;

How might the overloaded operator new and operator delete affect the performance of the code below?

int main(void)
{
  const int SIZE = 1000000;
  int i;

    // Allocate lots of pointers to Foo objects
  Foo **pf = new Foo*[SIZE];

    // Allocate lots of Foo objects  
  for (i = 0; i < SIZE; ++i)
    pf[i] = new Foo(i, i, i);

    // Delete lots of Foo objects
  for (i = 0; i < SIZE; ++i)
    delete pf[i];

  return 0;
}
Tests done on a 2.66 GHz P4. Time is in milliseconds:
            MS6   GNU3.3   BCC5.6   MS7.1
----------------------------------------             
Global new: 390     532     156      453
Foo::new  :  36      47      62       93
A more memory-efficient way to provide a convenient "next" pointer:

union {
  int a_;
  Foo *next_;
};
int b_, c_;


What Happens If operator new Fails?

Which code is correct?
const int BIG_NUM = 1000 * 1000 * 1000 * 2;

  // This style expects operator new to return NULL on failure
char *p1 = ::new char[BIG_NUM];
if (!p1)
  std::cout << "::operator new failed, returned NULL" << std::endl;


  // This style expects operator new to throw an exception on failure
char *p2;
try {
  p2 = ::new char[BIG_NUM];
}
catch (std::bad_alloc &e) {
  std::cout << "::operator new failed: " << e.what() << std::endl;
}
To suppress ::operator new from throwing:
char *p = ::new (std::nothrow) char[BIG_NUM];
To force ::operator new to throw:
Go here

What happens if a constructor throws an exception? Will the destructor be called? For example:

Foo x(1, 2, 3); // Foo's constructor throws an exception
will the compiler generate code to call x.~Foo()?

Another example:

int count = 0;  // global

class T
{
public:
  T() { 
    std::cout << "In T constructor\n";

      // throw exception on 3rd construction
    if (++::count == 3) 
      throw "No soup for you!";

  };
  ~T() {
    std::cout << "In T destructor\n";
  }
};
Given the class T above, what does this code print?
int main(void)
{
  try {
    T *pt = ::new T[5];
    ::delete [] pt;
  }
  catch (const char *e) {
    std::cout << e << std::endl;
  }

  return 0;
}
The output:
In T constructor
In T constructor
In T constructor
In T destructor
In T destructor
No soup for you!
Assembly details.