How C++ Placement New Works

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 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"?

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);

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.

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.

Placement New on already existing object in SharedMemory

I am essentially making the assumption that placement new is working
as if I placed a C struct in memory in this particular scenario via:
struct SHMArray* array = (struct SHMArray*)data;.. Is this correct?

No, this is not correct. Placement new also invokes the object's appropriate constructor. "struct SHMArray* array = (struct SHMArray*)data;" does not invoke any object's constructor. It's just a pointer conversion cast. Which does not invoke anyone's constructor. Key difference.

In your sample code, you do actually want to invoke the templated object's constructor. Although the shown example has other issues, as already mentioned in the comments, this does appear to be what needs to be done in this particular situation.

But insofar as the equivalent of placement new versus a pointer cast, no they're not the same. One invokes a constructor, one does not. new always invokes the constructor, whether it's placement new, or not. This is a very important detail, that's not to be overlooked.



Related Topics



Leave a reply



Submit