Placement syntax
Encyclopedia
In the C++
programming language
, placement syntax allows programmers to explicitly specify the memory management
of individual objects — i.e. their "placement" in memory. Normally, when an object is created dynamically, an allocation function is invoked in such a way that it will both allocate memory for the object, and initialize the object within the newly allocated memory. The placement syntax allows the programmer to supply additional arguments to the allocation function. A common use is to supply a pointer to a suitable region of storage where the object can be initialized, thus separating memory allocation from object construction.
The "placement" versions of the
Any
doesn't mention this technique. Support for placement new operator has been added to compilers circa 1995.
The placement syntax adds an expression list immediately after the
There is no placement delete expression.
void * operator new (size_t) throw(std::bad_alloc);
void * operator new[] (size_t) throw(std::bad_alloc);
The Standard C++ library provides two placement overloads each for these functions. Their declarations are:
void * operator new (size_t, const std::nothrow_t &) throw;
void * operator new (size_t, void *) throw;
void * operator new[] (size_t, const std::nothrow_t &) throw;
void * operator new[] (size_t, void *) throw;
In all of the overloads, the first parameter to the
There are also placement delete functions. They are overloaded versions of the non-placement delete functions. The non-placement delete functions are declared as:
void operator delete (void *) throw;
void operator delete[] (void *) throw;
The Standard C++ library provides two placement overloads each for these functions. Their declarations are:
void operator delete (void *, const std::nothrow_t &) throw;
void operator delete (void *, void *) throw;
void operator delete[] (void *, const std::nothrow_t &) throw;
void operator delete[] (void *, void *) throw;
In all of the overloads, the first parameter to the
For both the new and the delete functions, the functions are global, are not in any namespace, and do not have static linkage.
void * operator new (size_t, void * p) throw { return p ; }
void * operator new[] (size_t, void * p) throw { return p ; }
void operator delete (void *, void *) throw { }
void operator delete[] (void *, void *) throw { }
Default placement does not require the inclusion of the Standard C++ library header
There are various uses for default placement.
Bjarne Stroustrup
originally observed, in his book The Design and Evolution of C++, that pointer placement new is necessary for hardware that expects a certain object at a specific hardware address. It is also required for the construction of objects that need to reside in a certain memory area, such as an area that is shared between several processors of a multiprocessor computer.
Other uses, however, include calling a constructor directly, something which the C++ language does not otherwise permit.
The C++ language does allow a program to call a destructor
directly, and, since it is not possible to destroy the object using a
p->~T ;
Programmers who wish to do this in their programs must include the Standard C++ library header
struct T {} ;
int main
{
// Call the function operator new(size_t, const nothrow_t &) and (if successful) construct the object.
T * p = new (std::nothrow) T ;
if (p) {
// The storage has been allocated and the constructor called.
delete p ;
} else
; // An error has occurred. No storage has been allocated and no object constructed.
return 0 ;
}
class A {
public:
void * allocate ( size_t ) ;
void deallocate ( void * ) ;
} ;
And define custom placement allocation and deallocation functions as follows:
void *
operator new (size_t size, A & arena)
{
return arena.allocate(size) ;
}
void
operator delete (void * p, A & arena)
{
arena.deallocate(p) ;
}
The program would employ the placement syntax to allocate objects using different instances of the
A first_arena, second_arena ;
T * p1 = new (first_arena) T ;
T * p2 = new (second_arena) T ;
Destroying an object whose storage is allocated in such a fashion requires some care. Because there is no placement delete expression, one cannot use it to invoke the custom deallocator. One must either write a destruction function that invokes the custom deallocator, or call the placement delete function directly, as a function call.
The former would resemble:
void
destroy (T * p, A & arena)
{
p->~T ; // First invoke the destructor explicitly.
arena.deallocate(p) ; // Then call the deallocator function directly.
}
which would be invoked from a program as:
A arena ;
T * p = new (arena) T ;
/* ... */
destroy(p, arena) ;
The latter would involve simply writing the destructor invocation and delete function call into the program:
A arena ;
T * p = new (arena) T ;
/* ... */
p->~T ; // First invoke the destructor explicitly.
operator delete(p, arena) ; // Then call the deallocator function indirectly via operator delete(void *, A &) .
A common error is to attempt to use a delete expression to delete the object. This results in the wrong
void * operator new (size_t size, const char* file, int line);
void * operator new[] (size_t size, const char* file, int line);
void operator delete (void * p, const char* file, int line);
void operator delete[] (void * p, const char* file, int line);
This would be employed in a program as follows:
T * p = New T ;
The custom-written placement new functions would then handle using the supplied file and line number information in the event of an exception. For example:
class NewError {
public:
NewError(const char * file, int line) { /* ... */ }
/* ... */
} ;
void *
operator new (size_t size, const char* file, int line)
{
if (void * p = ::operator new (size, std::nothrow))
return p ;
throw NewError(file, line) ;
}
The placement delete functions are called from placement
of the object throws an exception. In such a circumstance, in order to ensure that the program does not incur a memory leak, the placement delete functions are called. A placement new expression first calls the placement
The placement delete function that is called matches the placement new function that was invoked by the placement new expression. So, for example, if the following code is executed, the placement delete function that is called will be
struct A {} ;
struct E {} ;
class T {
public:
T { throw E ; }
} ;
void * operator new ( size_t, const A & ) ;
void operator delete ( void *, const A & ) ;
int main
{
A a ;
T * p = new (a) T ;
return 0 ;
}
This is why the pointer placement delete functions are defined as no-operations by the Standard C++ library. Since the pointer placement new functions do not allocate any storage, there is no storage to be deallocated in the event of the object's constructor throwing an exception.
If no matching placement delete function exists, no deallocation function is called in the event of an exception being thrown by a constructor within a placement
C++
C++ is a statically typed, free-form, multi-paradigm, compiled, general-purpose programming language. It is regarded as an intermediate-level language, as it comprises a combination of both high-level and low-level language features. It was developed by Bjarne Stroustrup starting in 1979 at Bell...
programming language
Programming language
A programming language is an artificial language designed to communicate instructions to a machine, particularly a computer. Programming languages can be used to create programs that control the behavior of a machine and/or to express algorithms precisely....
, placement syntax allows programmers to explicitly specify the memory management
Memory management
Memory management is the act of managing computer memory. The essential requirement of memory management is to provide ways to dynamically allocate portions of memory to programs at their request, and freeing it for reuse when no longer needed. This is critical to the computer system.Several...
of individual objects — i.e. their "placement" in memory. Normally, when an object is created dynamically, an allocation function is invoked in such a way that it will both allocate memory for the object, and initialize the object within the newly allocated memory. The placement syntax allows the programmer to supply additional arguments to the allocation function. A common use is to supply a pointer to a suitable region of storage where the object can be initialized, thus separating memory allocation from object construction.
The "placement" versions of the
new
and delete
operators and functions are known as placement new
and placement delete
. A new
expression, placement or otherwise, calls a new
function, also known as an allocator function, whose name is operator new
. Similarly, a delete
expression calls a delete
function, also known as a deallocator function, whose name is operator delete
.Any
new
expression that uses the placement syntax is a placement new
expression, and any operator new
or operator delete
function that takes more than the mandatory first parameter ( and , respectively) is a placement new or placement delete function.History
In earlier versions of C++ there was no such thing as placement new; instead, developers used explicit assignment to this within constructors to achieve similar effect. This practice has been deprecated and abolished later, and third edition of The C++ Programming LanguageThe C++ Programming Language
The C++ Programming Language was the first book to describe the C++ programming language, written by the language’s creator, Bjarne Stroustrup, and first published in October 1985...
doesn't mention this technique. Support for placement new operator has been added to compilers circa 1995.
Expressions
The Standard C++ syntax for a non-placementnew
expression is
new
new-type-id ( optional-initializer-expression-list )
The placement syntax adds an expression list immediately after the
new
keyword. This expression list is the placement. It can contain any number of expressions.
new
( expression-list ) new-type-id ( optional-initializer-expression-list )
There is no placement delete expression.
Functions
The placement new functions are overloads of the non-placement new functions. The declaration of the non-placement new functions, for non-array and arraynew
expressions respectively, are:
void * operator new (size_t) throw(std::bad_alloc);
void * operator new[] (size_t) throw(std::bad_alloc);
The Standard C++ library provides two placement overloads each for these functions. Their declarations are:
void * operator new (size_t, const std::nothrow_t &) throw;
void * operator new (size_t, void *) throw;
void * operator new[] (size_t, const std::nothrow_t &) throw;
void * operator new[] (size_t, void *) throw;
In all of the overloads, the first parameter to the
operator new
function is of type , which when the function is called will be passed as an argument the amount of memory, in bytes, to allocate. All of the functions must return type , which is a pointer to the storage that the function allocates.There are also placement delete functions. They are overloaded versions of the non-placement delete functions. The non-placement delete functions are declared as:
void operator delete (void *) throw;
void operator delete[] (void *) throw;
The Standard C++ library provides two placement overloads each for these functions. Their declarations are:
void operator delete (void *, const std::nothrow_t &) throw;
void operator delete (void *, void *) throw;
void operator delete[] (void *, const std::nothrow_t &) throw;
void operator delete[] (void *, void *) throw;
In all of the overloads, the first parameter to the
operator delete
function is of type , which is the address of the storage to deallocate.For both the new and the delete functions, the functions are global, are not in any namespace, and do not have static linkage.
Use
Placement syntax has four main uses: default placement, preventing exceptions, custom allocators, and debugging.Default placement
The placement overloads ofoperator new
and operator delete
that employ an additional parameter are used for default placement, also known as pointer placement. Their definitions by the Standard C++ library, which it is not permitted for a C++ program to replace or override, are:
void * operator new (size_t, void * p) throw { return p ; }
void * operator new[] (size_t, void * p) throw { return p ; }
void operator delete (void *, void *) throw { }
void operator delete[] (void *, void *) throw { }
Default placement does not require the inclusion of the Standard C++ library header
<new>
in the source code of a C++ program. However, g++ version 4.0 still requires the use of this header (this might be true of other versions of the compiler or other compilers too.)There are various uses for default placement.
Bjarne Stroustrup
Bjarne Stroustrup
Bjarne Stroustrup ; born December 30, 1950 in Århus, Denmark) is a Danish computer scientist, most notable for the creation and the development of the widely used C++ programming language...
originally observed, in his book The Design and Evolution of C++, that pointer placement new is necessary for hardware that expects a certain object at a specific hardware address. It is also required for the construction of objects that need to reside in a certain memory area, such as an area that is shared between several processors of a multiprocessor computer.
Other uses, however, include calling a constructor directly, something which the C++ language does not otherwise permit.
The C++ language does allow a program to call a destructor
Destructor (computer science)
In object-oriented programming, a destructor is a method which is automatically invoked when the object is destroyed...
directly, and, since it is not possible to destroy the object using a
delete
expression, that is how one destroys an object that was constructed via a pointer placement new expression. For example:
p->~T ;
Preventing exceptions
Normally, the (non-placement) new functions throw an exception, of typestd::bad_alloc
, if they encounter an error, such as exhaustion of all available memory. This was not how the functions were defined by Stroustrup's Annotated C++ Reference Manual, but was a change made by the standardization committee when the C++ language was standardized. The original behaviour of the functions, which was to return a pointer when an error occurred, is accessible via placement syntax.Programmers who wish to do this in their programs must include the Standard C++ library header
<new>
in the source code. This header declares the global std::nothrow
object, which is of type std::nothrow_t
(also declared in the header), which is used to call the overloaded new functions that are declared as taking as their second parameter. For example:
struct T {} ;
int main
{
// Call the function operator new(size_t, const nothrow_t &) and (if successful) construct the object.
T * p = new (std::nothrow) T ;
if (p) {
// The storage has been allocated and the constructor called.
delete p ;
} else
; // An error has occurred. No storage has been allocated and no object constructed.
return 0 ;
}
Custom allocators
Placement syntax is also employed for custom allocators. This does not use any of the allocator and deallocator functions from the Standard C++ library header<new>
, but requires that programmers write their own allocation and deallocation functions, overloaded for user-defined types. For example, one could define a memory management class as follows:
class A {
public:
void * allocate ( size_t ) ;
void deallocate ( void * ) ;
} ;
And define custom placement allocation and deallocation functions as follows:
void *
operator new (size_t size, A & arena)
{
return arena.allocate(size) ;
}
void
operator delete (void * p, A & arena)
{
arena.deallocate(p) ;
}
The program would employ the placement syntax to allocate objects using different instances of the
A
class as follows:
A first_arena, second_arena ;
T * p1 = new (first_arena) T ;
T * p2 = new (second_arena) T ;
Destroying an object whose storage is allocated in such a fashion requires some care. Because there is no placement delete expression, one cannot use it to invoke the custom deallocator. One must either write a destruction function that invokes the custom deallocator, or call the placement delete function directly, as a function call.
The former would resemble:
void
destroy (T * p, A & arena)
{
p->~T ; // First invoke the destructor explicitly.
arena.deallocate(p) ; // Then call the deallocator function directly.
}
which would be invoked from a program as:
A arena ;
T * p = new (arena) T ;
/* ... */
destroy(p, arena) ;
The latter would involve simply writing the destructor invocation and delete function call into the program:
A arena ;
T * p = new (arena) T ;
/* ... */
p->~T ; // First invoke the destructor explicitly.
operator delete(p, arena) ; // Then call the deallocator function indirectly via operator delete(void *, A &) .
A common error is to attempt to use a delete expression to delete the object. This results in the wrong
operator delete
function being called. Dewhurst recommends two strategies for avoiding this error. The first is to ensure that any custom allocators rely upon the Standard C++ library's global, non-placement, operator new
, and are thus nothing more than simple wrappers around the C++ library's memory management. The second is to create new and delete functions for individual classes, and customize memory management via class function members rather than by using the placement syntax.Debugging
Placement new can also be used as a simple debugging tool, to enable programs to print the filename and line number of the source code where a memory allocation has failed. This does not require the inclusion of the Standard C++ library header<new>
, but does require the inclusion of a header that declares four placement functions and a macro replacement for the new
keyword that is used in new expressions. For example, such a header would contain:
void * operator new (size_t size, const char* file, int line);
void * operator new[] (size_t size, const char* file, int line);
void operator delete (void * p, const char* file, int line);
void operator delete[] (void * p, const char* file, int line);
- define New new(__FILE__, __LINE__)
- else
- define New new
- endif
This would be employed in a program as follows:
T * p = New T ;
The custom-written placement new functions would then handle using the supplied file and line number information in the event of an exception. For example:
class NewError {
public:
NewError(const char * file, int line) { /* ... */ }
/* ... */
} ;
void *
operator new (size_t size, const char* file, int line)
{
if (void * p = ::operator new (size, std::nothrow))
return p ;
throw NewError(file, line) ;
}
Placement delete
As noted above, there is no placement delete expression. It is not possible to call any placementoperator delete
function using a delete
expression.The placement delete functions are called from placement
new
expressions. In particular, they are called if the constructorConstructor (computer science)
In object-oriented programming, a constructor in a class is a special type of subroutine called at the creation of an object. It prepares the new object for use, often accepting parameters which the constructor uses to set any member variables required when the object is first created...
of the object throws an exception. In such a circumstance, in order to ensure that the program does not incur a memory leak, the placement delete functions are called. A placement new expression first calls the placement
operator new
function, then calls the constructor of the object upon the raw storage returned from the allocator function. If the constructor throws an exception, it is necessary to deallocate that storage before propagating the exception back to the code that executed the placement new expression, and that is the purpose of the placement delete functions.The placement delete function that is called matches the placement new function that was invoked by the placement new expression. So, for example, if the following code is executed, the placement delete function that is called will be
operator delete(void *, const A &)
:
struct A {} ;
struct E {} ;
class T {
public:
T { throw E ; }
} ;
void * operator new ( size_t, const A & ) ;
void operator delete ( void *, const A & ) ;
int main
{
A a ;
T * p = new (a) T ;
return 0 ;
}
This is why the pointer placement delete functions are defined as no-operations by the Standard C++ library. Since the pointer placement new functions do not allocate any storage, there is no storage to be deallocated in the event of the object's constructor throwing an exception.
If no matching placement delete function exists, no deallocation function is called in the event of an exception being thrown by a constructor within a placement
new
expression. There are also some (older) C++ implementations that do not support placement delete (which, like the exception-throwing allocator functions, were an addition made to C++ when it was standardized) at all. In both such situations, an exception being thrown by a constructor when allocating using a custom allocator will result in a memory leak. (In the case of the older C++ implementations, a memory leak will also occur with non-placement new
expressions.)