What uses are there for placement new?
Placement new allows you to construct an object in memory that's already allocated.
You may want to do this for optimization when you need to construct multiple instances of an object, and it is faster not to re-allocate memory each time you need a new instance. Instead, it might be more efficient to perform a single allocation for a chunk of memory that can hold multiple objects, even though you don't want to use all of it at once.
DevX gives a good example:
Standard C++ also supports placement
new operator, which constructs an
object on a pre-allocated buffer. This
is useful when building a memory pool,
a garbage collector or simply when
performance and exception safety are
paramount (there's no danger of
allocation failure since the memory
has already been allocated, and
constructing an object on a
pre-allocated buffer takes less time):
char *buf = new char[sizeof(string)]; // pre-allocated buffer
string *p = new (buf) string("hi"); // placement new
string *q = new string("hi"); // ordinary heap allocation
You may also want to be sure there can be no allocation failure at a certain part of critical code (for instance, in code executed by a pacemaker). In that case you would want to allocate memory earlier, then use placement new within the critical section.
Deallocation in placement new
You should not deallocate every object that is using the memory buffer. Instead you should delete[] only the original buffer. You would have to then call the destructors of your classes manually. For a good suggestion on this, please see Stroustrup's FAQ on: Is there a "placement delete"?
How C++ placement new works?
It's really, really simple: new
can be thought of as doing two things:
- Allocating the memory.
- Placement-constructing the object in the allocated memory.
There's no guarantee that malloc
is actually used by the implementation, but typically it is. You cannot assume it about the implementation, but for the purpose of understanding it's an OK assumption.
Thus, the following are thought of as being equivalent:
auto obj1 = new std::string("1");
// ↑ can be thought of as equivalent to ↓
auto obj2 = (std::string*)malloc(sizeof(std::string));
new(obj2) std::string("2");
Same goes for delete
:
delete obj1;
// ↑ can be thought of as equivalent to ↓
obj2->~string();
free(obj2);
You can then easily reason about it all when you see new
and delete
for what they really are: an allocation followed by constructor call, and a destructor call followed by deallocation.
When you use placement new
, you've decided to take care of the first step separately. The memory has to be still allocated somehow, you just get to have full control over how it happens and where does the memory come from.
You thus must keep track of two things, separately:
The lifetime of the memory.
The lifetime of the object.
The code below demonstrates how these are independent of each other:
#include <cstdlib>
#include <string>
#include <new>
using std::string;
int main() {
auto obj = (string*)malloc(sizeof(string)); // memory is allocated
new(obj) string("1"); // string("1") is constructed
obj->~string (); // string("1") is destructed
new(obj) string("2"); // string("2") is constructed
obj->~string (); // string("2") is destructed
free(obj); // memory is deallocated
}
Your program has UB if the lifetime of the object extends past the lifetime of memory. Make sure that the memory always outlives the life of the object. For example, this has UB:
void ub() {
alignas(string) char buf[sizeof(string)]; // memory is allocated
new(buf) string("1"); // string("1") is constructed
} // memory is deallocated but string("1") outlives the memory!
But this is OK:
void ub() {
alignas(string) char buf[sizeof(string)]; // memory is allocated
new(buf) string("1"); // string("1") is constructed
buf->~string(); // string("1") is destructed
} // memory is deallocated
Note how you need to properly align the automatic buffer using alignas
. The lack of alignas
for an arbitrary type results in UB. It might appear to work, but that's only to mislead you.
There are some specific types where not calling the destructor and not aligning the memory properly does not lead to UB, but you should never assume such things about a type. Call your destructors and do the alignment, it won't cost you anything if it turns out to be unnecessary - no extra code would be generated for such a type.
struct S {
char str[10];
}
What is a placement new?
What is a placement new?
It constructs a dynamic object into provided area of storage.
When we say placement new, do we refer to new expression
Yes, but specifically to a new expression where the placement parameter has not been omitted.
"Placement syntax" is also used to refer to this.
or operator new (function)?
...also yes although perhaps less often. It depends on context. New expression will call one of the new operators before initialising the object into the address returned by the operator. Placement new expression calls a placement operator new.
The standard placement operator new does nothing and returns the pointer argument unchanged.
Example:
Following is a new expression where placement new parameter has been omitted. This allocates storage (non-placement operator new will be invoked) and creates an object into that storage.
T* p1 = new T();
// ^ no placement parameter
delete p1; // don't forget to clean up
Following is not a new expression, but a call to a non-placement global operator new.
void* storage = ::operator new(sizeof(T), alignof(T));
Following is a new expression with a placement parameter. This is the "placement new". It creates an object without allocating storage.
T* p2 = new (storage) T(); // must include <new>
// ^^^^^^^^^ placement parameter
p2->~T(); // don't forget to clean up
::operator delete(storage);
Note: Placement new doesn't necessarily have to construct the object into dynamically allocated memory.
P.S. Starting from C++11, std::allocator_traits<std::allocator>::construct
can be used in place of placement new, and since C++20 there is std::construct_at
. Unlike placement new, these alternatives are constexpr (since C++20).
Is this a placement new even the first parameter is not a pointer?
Yes, the new(5u)int
is still a placement new expression, regardless of the type of the placement parameter.
There is no standard placement operator new accepting anything other than void*
, but the user defined placement operator new such as one shown in the question would allow using such expression.
This is rather obscure feature of the language. I've never seen this used in practice.
P.S. The shown operator new is practically broken because it returns null.
What is an in-place constructor in C++?
This is called the placement new operator. It allows you to supply the memory the data will be allocated in without having the new
operator allocate it. For example:
Foo * f = new Foo();
The above will allocate memory for you.
void * fm = malloc(sizeof(Foo));
Foo *f = new (fm) Foo();
The above will use the memory allocated by the call to malloc
. new
will not allocate any more. You are not, however, limited to classes. You can use a placement new operator for any type you would allocate with a call to new
.
A 'gotcha' for placement new is that you should not release the memory allocated by a call to the placement new operator using the delete
keyword. You will destroy the object by calling the destructor directly.
f->~Foo();
After the destructor is manually called, the memory can then be freed as expected.
free(fm);
Why should I use placement new?
the normal (nonplacement) new
is basically equivalent to doing
T* ptr = static_cast<T*>(malloc(sizeof(T)));
new(ptr) T;
Of course the reality looks a bit different due to errorchecking and such, but the result is more or less the same (through not identical, you can't delete
a pointer allocated that way, instead you need to call the destructor explicitely (ptr->~T()
) and then release the memory using free
).
So placement new should indeed be faster then non placement new, since it doesn't need to allocate the memory. However the problem is that the memory needs to be allocated somewhere. So you have essentially replaced one call to new
with a call to placement new
and some code for the allocation somewhere (if not why would you use new
in the first place?). It should be obvious that this is less convinient and more bug prone.
Now of course you can write a faster allocation method, but for that you typically need to do some sort of tradeoff. It's not going to be easy to write a allocator which is faster without either using more memory (extra data for faster identification of free blocks) or making it very specific (writing fast allocation of a single objectsize is much easier then a general one). In the end it is typically not worth the effort (for scenarious where it is worth the effort it has likely already been done, so you could use an existing allocator (which likely uses placement new internally)).
There are of course uses for placement new (sometimes you do have the memory preallocated), but that is simply not the common case
c++ allocator: operator new or placement new
You have to differ between the operator new
function and new
expressions.
The former (the operator new
function) only allocates memory. The latter (new
expression) calls operator new
to allocate memory and then invokes the constructor.
std::allocator
have two functions to implement allocation and construction: allocate
and construct
.
The allocate
function uses the operator new
function to allocate memory. The construct
function uses placement-new to construct objects. Which seems to be the two-step process you want.
Related Topics
Can Std::Vector Emplace_Back Copy Construct from an Element of the Vector Itself
When Is Explicit Move Needed for a Return Statement
Handling Overflow When Casting Doubles to Integers in C
How to Avoid Entering Library's Source Files While Debugging in Qt Creator with Gdb
Cost of Throwing C++0X Exceptions
Why Does Constexpr Static Member (Of Type Class) Require a Definition
How to Implement No-Op MACro (Or Template) in C++
Right Shift and Signed Integer
How to Avoid Undefined Execution Order for the Constructors When Using Std::Make_Tuple
How to Test an Exe with Google Test
Why Does the Main Function Work with No Return Value
Are C++ Libs Created with Different Versions of Visual Studio Compatible with Each Other
Why Sizeof Built in Types Except Char Is Compiler Dependent in C & C++
Convert a Static Library to a Shared Library (Create Libsome.So from Libsome.A): Where's My Symbols
Cin.Getline() Is Skipping an Input in C++
How to Detect Whether Windows Is Shutting Down or Restarting