I'm just going to direct you to this answer: What is the difference between new/delete and malloc/free? . Martin provided an excellent overview. Quick overview on how they work (without diving into how you could overload them as member functions):
new-expression and allocation
- The code contains a new-expression supplying the type-id.
- The compiler will look into whether the type overloads the operator new with an allocation function.
- If it finds an overload of an operator new allocation function, that one is called using the arguments given to new and sizeof(TypeId) as its first argument:
Sample:
new (a, b, c) TypeId;
// the function called by the compiler has to have the following signature:
operator new(std::size_t size, TypeOfA a, TypeOfB b, TypeOf C c);
- if operator new fails to allocate storage, it can call
new_handler
, and hope it makes place. If there still is not enough place, new has to throw std::bad_alloc
or derived from it. An allocator that has throw()
(no-throw guarantee), it shall return a null-pointer in that case.
- The C++ runtime environment will create an object of the type given by the type-id in the memory returned by the allocation function.
There are a few special allocation functions given special names:
no-throw
new. That takes a nothrow_t
as second argument. A new-expression of the form like the following will call an allocation function taking only std::size_t and nothrow_t:
Example:
new (std::nothrow) TypeId;
placement new
. That takes a void* pointer as first argument, and instead of returning a newly allocated memory address, it returns that argument. It is used to create an object at a given address. Standard containers use that to preallocate space, but only create objects when needed, later.
Code:
// the following function is defined implicitly in the standard library
void * operator(std::size_t size, void * ptr) throw() {
return ptr;
}
If the allocation function returns storage, and the the constructor of the object created by the runtime throws, then the operator delete is called automatically. In case a form of new was used that takes additional parameters, like
new (a, b, c) TypeId;
Then the operator delete that takes those parameters is called. That operator delete version is only called if the deletion is done because the constructor of the object did throw. If you call delete yourself, then the compiler will use the normal operator delete function taking only a void*
pointer:
int * a = new int;
=> void * operator new(std::size_t size) throw(std::bad_alloc);
delete a;
=> void operator delete(void * ptr) throw();
TypeWhosCtorThrows * a = new ("argument") TypeWhosCtorThrows;
=> void * operator new(std::size_t size, char const* arg1) throw(std::bad_alloc);
=> void operator delete(void * ptr, char const* arg1) throw();
TypeWhosCtorDoesntThrow * a = new ("argument") TypeWhosCtorDoesntThrow;
=> void * operator new(std::size_t size, char const* arg1) throw(std::bad_alloc);
delete a;
=> void operator delete(void * ptr) throw();
new-expression and arrays
If you do
new (possible_arguments) TypeId[N];
The compiler is using the operator new[]
functions instead of plain operator new
. The operator can be passed a first argument not exactly sizeof(TypeId)*N
: The compiler could add some space to store the number of objects created (necassary to be able to call destructors). The Standard puts it this way:
new T[5]
results in a call of operator new[](sizeof(T)*5+x)
, and
new(2,f) T[5]
results in a call of operator new[](sizeof(T)*5+y,2,f)
.