C++ 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];
}

placement new equivalent in C

There is no such thing as "placement new" in C. On the other hand, you can declare a pointer and make it point to anything in memory:

char *pointer = 0x12345678;

The above pointer now points to whatever is as the address 0x12345678.

This is often used in embedded systems, where certain devices are at certain places in memory.

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.

Placement new and alignment in C++

Yes, the assertion will hold. Any new expression creating a single object must request exactly sizeof(Test) bytes of storage from the allocation function; and so it must place the object at the start of that storage in order to have enough room.

Note: This is based on the specification of a new-expression in C++11. It looks like C++14 will change the wording, so the answer may be different in the future.

Placement New in C++

Everything is right, until...

pc3 = new (buffer) JustTesting("Bad Idea", 6);

This invokes undefined behaviour (no?). You've already constructed an object of type JustTesting at buffer, but you have not destructed it! At the same time, you're creating yet another object at the same location. Then, the first object becomes corrupted (althought, in standardese minds, it still exists in a parallel universe).

You can't perform delete on a pointer to anything that hasn't been allocated (and constructed) by operator new. Similarly, you can only destroy and deallocate an array created by operator new[] with operator delete[].

Now, "placement new" is just a fancy name for a direct call to a constructor. Thus, new(buff) Type(...) is just a call to Types constructor with this set as buff. And, simmetric with what said above, you can only destroy what has been constructed.

If you use automatic storage, operator new, or whatever other implicit-RAII-conformant means that is responsible for allocating, constructing, and destructing your objects automatically (or when you specify it shall be done), then calling an object's destructor in such a context will lead to the destructor being called twice, a.k.a undefined behaviour.

Now, it happens that you (shall I repeat it again? you!) are the one who decides when and how to obtain the memory for the object, then the environment has no change of guessing when to either destroy or deallocate the object. Thus, once you call the object's destructor explicitly, the memory that once contained it is under your responsibility for being freed, somehow, if at all.

Think of it as such. An expression of the form ptr = new X(...) can be perfectly implemented as...

ptr = malloc(sizeof(X));
new(ptr) X(...);

And operator delete becomes...

ptr->~X();
free(ptr);

C++ in-place placement new

No you cannot do that (safely). Placement new must be used on an allocated but uninitialized memory or on a POD object.

You can't just bypass constructors, copy, move ctors/assignments and destructors on objects (except for PODs). If you want to do that, there is something wrong on your design and you need to go to the drawing board.



Related Topics



Leave a reply



Submit