What Uses Are There For "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];
}

Using placement new in generic programming

(This answer uses N4659, the final C++17 draft.)

Why ::new is used instead of just new

::new ensures that the operator new is looked up in the global scope. In contrast, the plain new first looks up in the scope of the class if T is a class type (or array thereof), and only then falls back to the global scope. Per [expr.new]/9:

If the new-expression begins with a unary ​::​ operator, the
allocation function's name is looked up in the global scope.
Otherwise, if the allocated type is a class type T or array thereof,
the allocation function's name is looked up in the scope of T. If
this lookup fails to find the name, or if the allocated type is not a
class type, the allocation function's name is looked up in the global
scope.

For example, with

struct C {
void* operator new(std::size_t, void* ptr) noexcept
{
std::cout << "Hello placement new!\n";
return ptr;
}
};

The plain new will cause this function to be found, thus printing unwanted message, whereas ::new will still find the global function and work properly.

The global operator new(std::size_t, void*) cannot be replaced because of [new.delete.placement]/1:

These functions are reserved; a C++ program may not define functions that displace the versions in the C++ standard library
([constraints]).
The provisions of [basic.stc.dynamic] do not apply
to these reserved placement forms of operator new and operator
delete
.

(See How should I write ISO C++ Standard conformant custom new and delete operators? for more about overloading operator new.)

Why an explicit cast to void* is required

Although the global operator new(std::size_t, void*) may not be replaced, new versions of ::operator new can be defined. For example, suppose that the following declaration is placed in the global scope:

void* operator new(std::size_t, int* ptr) noexcept
{
std::cout << "Hello placement new!\n";
return ptr;
}

Then ::new(ptr) T will use this version instead of the global version, where ptr is a int* value. The pointer is explicitly cast to void* to ensure that the void* version of operator new (which we intend to call) wins in overload resolution.


From comment:

But why do we want to call exactly global new for void* if some
type has special overload of new for itself? Seems like normal
overloaded operator is more suitable - why it's not?

Normally, new is used for allocation purposes. Allocation is something the user should have control over. The user can roll out more suitable versions for a normal new.

In this case, however, we don't want to allocate anything — all we want to do is create an object! The placement new is more of a "hack" — its presence is largely due to the lack of syntax available for constructing an object at a specified address. We don't want the user to be able to customize anything. The language itself, however, doesn't care about this hack, though — we have to treat it specially. Of course, if we have something like construct_at (which is coming in C++20), we will use it!

Also note that std::uninitialized_copy is intended for the simplest case where you just want to copy construct a sequence of objects in raw allocated space. The standard containers allow you to customize not only how the elements are allocated, but also how they are constructed, by means of an allocator. Therefore, they do not generally use std::uninitialized_copy for their elements — they call std::allocator_traits<Allocator>::construct. This feature is used by std::scoped_allocator_adaptor.

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

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 makes placement new call the constructor of an object?

But what makes it call the constructor?

The semantics of operator new

For details, read the C++11 standard n3337 about new expressions, or a newer variant of that standard.

When you define some operator new, using it later would call the constructor. By definition of C++.

I recommend reading a good C++ programming book.

Exercise: define the usual operator new (in your class MyClass) using malloc, the placement new, and some throw expression.



Related Topics



Leave a reply



Submit