Free store (C++ only)

Free store is a pool of memory available for you to allocate (and deallocate) storage for objects during the execution of your program. The new and delete operators are used to allocate and deallocate free store, respectively.

You can define your own versions of new and delete for a class by overloading them. You can declare the new and delete operators with additional parameters. When new and delete operate on class objects, the class member operator functions new and delete are called, if they have been declared.

If you create a class object with the new operator, one of the operator functions operator new() or operator new[]() (if they have been declared) is called to create the object. An operator new() or operator new[]() for a class is always a static class member, even if it is not declared with the keyword static. It has a return type void* and its first parameter must be the size of the object type and have type std::size_t. It cannot be virtual.

Type std::size_t is an implementation-dependent unsigned integral type defined in the standard library header <cstddef>. When you overload the new operator, you must declare it as a class member, returning type void*, with its first parameter of type std::size_t. You can declare additional parameters in the declaration of operator new() or operator new[](). Use the placement syntax to specify values for these parameters in an allocation expression.

The following example overloads two operator new functions:

#include <new>
#include <iostream>

using namespace std;

class X;

struct Node {
  X* data;
  bool filled;
  Node() : filled(false) { }
};

class X {
  static Node buffer[];

public:

  int number;

  enum { size = 3};

  void* operator new(size_t sz) throw (const char*) {
    void* p = malloc(sz);
    if (sz == 0) throw "Error: malloc() failed";
    cout << "X::operator new(size_t)" << endl;
    return p;
  }

  void *operator new(size_t sz, int location) throw (const char*) {
    cout << "X::operator new(size_t, " << location << ")" << endl;
    void* p = 0;
    if (location < 0 || location >= size || buffer[location].filled == true) {
      throw "Error: buffer location occupied";
    }
    else {
 	  p = malloc(sizeof(X));
	  if (p == 0) throw "Error: Creating X object failed";
	  buffer[location].filled = true;
      buffer[location].data = (X*) p;
    }
    return p;
  }

  static void printbuffer() {
    for (int i = 0; i < size; i++) {
      cout << buffer[i].data->number << endl;
    }
  }

};

Node X::buffer[size];

int main() {
  try {
    X* ptr1 = new X;
    X* ptr2 = new(0) X;
    X* ptr3 = new(1) X;
    X* ptr4 = new(2) X;
    ptr2->number = 10000;
    ptr3->number = 10001;
    ptr4->number = 10002;
    X::printbuffer();
    X* ptr5 = new(0) X;
  }
  catch (const char* message) {
    cout << message << endl;
  }
}

The following is the output of the above example:

X::operator new(size_t)
X::operator new(size_t, 0)
X::operator new(size_t, 1)
X::operator new(size_t, 2)
10000
10001
10002
X::operator new(size_t, 0)
Error: buffer location occupied

The statement X* ptr1 = new X calls X::operator new(sizeof(X)). The statement X* ptr2 = new(0) X calls X::operator new(sizeof(X),0).

The delete operator destroys an object created by the new operator. The operand of delete must be a pointer returned by new. If delete is called for an object with a destructor, the destructor is invoked before the object is deallocated.

If you destroy a class object with the delete operator, the operator function operator delete() or operator delete[]() (if they have been declared) is called to destroy the object. An operator delete() or operator delete[]() for a class is always a static member, even if it is not declared with the keyword static. Its first parameter must have type void*. Because operator delete() and operator delete[]() have a return type void, they cannot return a value.

The following example shows the declaration and use of the operator functions operator new() and operator delete():

#include <cstdlib>
#include <iostream>
using namespace std;

class X {
public:
  void* operator new(size_t sz) throw (const char*) {
    void* p = malloc(sz);
    if (p == 0) throw "malloc() failed";
    return p;
  }

  // single argument
  void operator delete(void* p) {
	cout << "X::operator delete(void*)" << endl;
    free(p);
  }

};

class Y {
  int filler[100];
public:

  // two arguments
  void operator delete(void* p, size_t sz) throw (const char*) {
    cout << "Freeing " << sz << " byte(s)" << endl;
    free(p);
  };

};

int main() {
  X* ptr = new X;

  // call X::operator delete(void*)
  delete ptr;

  Y* yptr = new Y;

  // call Y::operator delete(void*, size_t)
  // with size of Y as second argument
  delete yptr;
}

The above example will generate output similar to the following:

X::operator delete(void*)
Freeing 400 byte(s)

The statement delete ptr calls X::operator delete(void*). The statement delete yptr calls Y::operator delete(void*, size_t).

The result of trying to access a deleted object is undefined because the value of the object can change after deletion.

If new and delete are called for a class object that does not declare the operator functions new and delete, or they are called for a nonclass object, the global operators new and delete are used. The global operators new and delete are provided in the C++ library.

The C++ operators for allocating and deallocating arrays of class objects are operator new[ ]() and operator delete[ ]().

You cannot declare the delete operator as virtual. However you can add polymorphic behavior to your delete operators by declaring the destructor of a base class as virtual. The following example demonstrates this:

#include <iostream>
using namespace std;

struct A {
  virtual ~A() { cout << "~A()" << endl; };
  void operator delete(void* p) {
    cout << "A::operator delete" << endl;
    free(p);
  }
};

struct B : A {
  void operator delete(void* p) {
    cout << "B::operator delete" << endl;
    free(p);
  }
};

int main() {
  A* ap = new B;
  delete ap;
}

The following is the output of the above example:

~A()
B::operator delete

The statement delete ap uses the delete operator from class B instead of class A because the destructor of A has been declared as virtual.

Although you can get polymorphic behavior from the delete operator, the delete operator that is statically visible must still be accessible even though another delete operator might be called. For example, in the above example, the function A::operator delete(void*) must be accessible even though the example calls B::operator delete(void*) instead.

Virtual destructors do not have any affect on deallocation operators for arrays (operator delete[]()). The following example demonstrates this:

#include <iostream>
using namespace std;

struct A {
  virtual ~A() { cout << "~A()" << endl; }
  void operator delete[](void* p, size_t) {
    cout << "A::operator delete[]" << endl;
    ::delete [] p;
  }
};

struct B : A {
  void operator delete[](void* p, size_t) {
    cout << "B::operator delete[]" << endl;
    ::delete [] p;
  }
};

int main() {
  A* bp = new B[3];
  delete[] bp;
};

The behavior of the statement delete[] bp is undefined.

When you overload the delete operator, you must declare it as class member, returning type void, with the first parameter having type void*, as described above. You can add a second parameter of type size_t to the declaration. You can only have one operator delete() or operator delete[]() for a single class.

Related information