
David A. answered 10/10/19
Experienced C++ tutor who loves to mentor and pass on my knowledge
Placement new is useful for when you already have memory allocated for an object, but wish to call an object's constructor. For example, you may want multiple instances of an object and not have to reallocate memory each time you need a new instance. It may be more efficient to reserve a single allocation for a chunk of memory that can hold multiple objects, even though you don't want to use them all at the same time. Placement new is mostly used in situations where a pool of memory is being managed.
Recall:
T *ptr = new T;
- First, memory is allocated to store type T
- Second, after memory has been allocated, the constructor is called
Placement new effectively says "I have memory allocated already; all I need is for the constructor to be called on the object that is located at the memory address that I pass to placement new.
Example:
#include <iostream>
class testPlacementNew {
int m_x;
int m_y;
public:
testPlacementNew() : m_x(0), m_y(0) {
std::cout << "Calling testPlacementNew default constructor\n";
}
testPlacementNew(int x, int y) : m_x(x), m_y(y) {
std::cout << "Calling testPlacementNew constructor with member initialization\n";
}
friend std::ostream &operator<<(std::ostream &os, testPlacementNew &obj) {
os << "X: " << obj.m_x << '\n';
os << "Y: " << obj.m_y << '\n';
return os;
}
~testPlacementNew() {
std::cout << "Calling ~testPlacementNew destructor @" << this << ":\n";
std::cout << *this; // print to prove we are accessing all objects
}
};
int main(int argc, const char * argv[]) {
constexpr int BUF_SIZE = 80;
constexpr int NUM_OBJECTS = BUF_SIZE / sizeof(testPlacementNew);
char buffer[BUF_SIZE];
// Pre-create NUM_OBJECTS testPlacementNew objects and call their
// constructors implicitly
//
// Note: We use reinterpret_cast here because we need to do pointer
// arithmetic on testPlacementNew objects that are stored in a (buffer)
// that is of (char *) pointer type. The two types are incompatible to
// each other, so we cannot use static_cast. The bufPtr assignment below is
// 'reinterpreting' the 'char *' buffer type to the testPlacementNew type,
// so that when we add the index (i), pointer arithmetic will add
// buffer + index*sizeof(testPlacementNew) and NOT sizeof(char)
//
for (int index = 0; index < NUM_OBJECTS; ++index) {
testPlacementNew *bufPtr =
reinterpret_cast<testPlacementNew*>(buffer) + index;
// placement new: I am ignoring the returned pointer from operator new
// because in this simple program, I will be using pointer offsets to
// calculate the position for each testPlacementNew object in the
// buffer :)
//
new(bufPtr) testPlacementNew(index,index); // initialize with the index
}
// Validate the objects got created correctly
//
for (int index = 0; index < NUM_OBJECTS; ++index) {
testPlacementNew *bufPtr =
reinterpret_cast<testPlacementNew*>(buffer) + index;
std::cout << *bufPtr << '\n';
}
//
//... Do whatever else I would want to do with these objects...
//
// We're done, BUT we need to call the destructors *explicitly* on our
// objects created by placement new because we can't use operator delete.
// operator delete would try to free memory that was not allocated by the
// regular operator new. Recall that placement new used pre-allocated
// memory...
//
for (int index = 0; index < NUM_OBJECTS; ++index) {
testPlacementNew *bufPtr =
reinterpret_cast<testPlacementNew*>(buffer) + index;
bufPtr->~testPlacementNew();
}
return 0;
}
Output when compiled and executed:
Calling testPlacementNew constructor with member initialization
Calling testPlacementNew constructor with member initialization
Calling testPlacementNew constructor with member initialization
Calling testPlacementNew constructor with member initialization
Calling testPlacementNew constructor with member initialization
Calling testPlacementNew constructor with member initialization
Calling testPlacementNew constructor with member initialization
Calling testPlacementNew constructor with member initialization
Calling testPlacementNew constructor with member initialization
Calling testPlacementNew constructor with member initialization
X: 0
Y: 0
X: 1
Y: 1
X: 2
Y: 2
X: 3
Y: 3
X: 4
Y: 4
X: 5
Y: 5
X: 6
Y: 6
X: 7
Y: 7
X: 8
Y: 8
X: 9
Y: 9
Calling ~testPlacementNew destructor @0x7ffeefbff470:
X: 0
Y: 0
Calling ~testPlacementNew destructor @0x7ffeefbff478:
X: 1
Y: 1
Calling ~testPlacementNew destructor @0x7ffeefbff480:
X: 2
Y: 2
Calling ~testPlacementNew destructor @0x7ffeefbff488:
X: 3
Y: 3
Calling ~testPlacementNew destructor @0x7ffeefbff490:
X: 4
Y: 4
Calling ~testPlacementNew destructor @0x7ffeefbff498:
X: 5
Y: 5
Calling ~testPlacementNew destructor @0x7ffeefbff4a0:
X: 6
Y: 6
Calling ~testPlacementNew destructor @0x7ffeefbff4a8:
X: 7
Y: 7
Calling ~testPlacementNew destructor @0x7ffeefbff4b0:
X: 8
Y: 8
Calling ~testPlacementNew destructor @0x7ffeefbff4b8:
X: 9
Y: 9
The buffer was allocated with 80 bytes (BUF_SIZE) of memory, using the index value as the initializer. Assuming the int data type takes 4 bytes, then sizeof(testPlacementNew) == 8 bytes, which means I have enough room to store BUF_SIZE/8 = 10 testPlacementNew objects :)
I hope this helps!