What Are Uses of the C++ Construct "Placement New"

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:

  1. Allocating the memory.
  2. 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:

  1. The lifetime of the memory.

  2. 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



Leave a reply



Submit