12 Special member functions [special]

12.5 Free store [class.free]

Any allocation function for a class T is a static member (even if not explicitly declared static).

Example:

class Arena;
struct B {
  void* operator new(std::size_t, Arena*);
};
struct D1 : B {
};

Arena*  ap;
void foo(int i) {
  new (ap) D1;      // calls B::operator new(std::size_t, Arena*)
  new D1[i];        // calls ::operator new[](std::size_t)
  new D1;           // ill-formed: ::operator new(std::size_t) hidden
}

 — end example ]

When an object is deleted with a delete-expression ([expr.delete]), a deallocation function (operator delete() for non-array objects or operator delete[]() for arrays) is (implicitly) called to reclaim the storage occupied by the object ([basic.stc.dynamic.deallocation]).

If a delete-expression begins with a unary :: operator, the deallocation function's name is looked up in global scope. Otherwise, if the delete-expression is used to deallocate a class object whose static type has a virtual destructor, the deallocation function is the one selected at the point of definition of the dynamic type's virtual destructor ([class.dtor]).117 Otherwise, if the delete-expression is used to deallocate an object of class T or array thereof, the static and dynamic types of the object shall be identical and the deallocation function's name is looked up in the scope of T. If this lookup fails to find the name, the name is looked up in the global scope. If the result of the lookup is ambiguous or inaccessible, or if the lookup selects a placement deallocation function, the program is ill-formed.

When a delete-expression is executed, the selected deallocation function shall be called with the address of the block of storage to be reclaimed as its first argument and (if the two-parameter style is used) the size of the block as its second argument.118

Any deallocation function for a class X is a static member (even if not explicitly declared static). Example:

class X {
  void operator delete(void*);
  void operator delete[](void*, std::size_t);
};

class Y {
  void operator delete(void*, std::size_t);
  void operator delete[](void*);
};

 — end example ]

Since member allocation and deallocation functions are static they cannot be virtual. [ Note: however, when the cast-expression of a delete-expression refers to an object of class type, because the deallocation function actually called is looked up in the scope of the class that is the dynamic type of the object, if the destructor is virtual, the effect is the same. For example,

struct B {
  virtual ~B();
  void operator delete(void*, std::size_t);
};

struct D : B {
  void operator delete(void*);
};

void f() {
  B* bp = new D;
  delete bp;        //1: uses D::operator delete(void*)
}

Here, storage for the non-array object of class D is deallocated by D::operator delete(), due to the virtual destructor.  — end note ] [ Note: Virtual destructors have no effect on the deallocation function actually called when the cast-expression of a delete-expression refers to an array of objects of class type. For example,

struct B {
  virtual ~B();
  void operator delete[](void*, std::size_t);
};

struct D : B {
  void operator delete[](void*, std::size_t);
};

void f(int i) {
  D* dp = new D[i];
  delete [] dp;     // uses D::operator delete[](void*, std::size_t)
  B* bp = new D[i];
  delete[] bp;      // undefined behavior
}

 — end note ]

Access to the deallocation function is checked statically. Hence, even though a different one might actually be executed, the statically visible deallocation function is required to be accessible. [ Example: for the call on line //1 above, if B::operator delete() had been private, the delete expression would have been ill-formed.  — end example ]

Note: If a deallocation function has no explicit exception-specification, it is treated as if it were specified with noexcept(true) ([except.spec]).  — end note ]

A similar provision is not needed for the array version of operator delete because [expr.delete] requires that in this situation, the static type of the object to be deleted be the same as its dynamic type.

If the static type of the object to be deleted is different from the dynamic type and the destructor is not virtual the size might be incorrect, but that case is already undefined; see [expr.delete].