An additional version of operator
new enables you to construct an
object or an array of objects at a predetermined memory position. This version
is called
placement new and has many useful applications, including
building a custom-made memory pool or a garbage collector. Additionally, it can
be used in mission-critical applications because there's no danger of
allocation failure; the memory that's used by placement
new has
already been allocated. Placement
new is also faster because the
construction of an object on a preallocated buffer takes less time.
You already know how to use placement
new
to allocate a single object on a predetermined memory address. However,
some programming tasks require the allocation of arrays on a
predetermined memory address. Here's how you do it.
Placement
new Overview
Mobile devices, embedded systems and custom garbage collectors are
only a few instances of programming environments that may require
placement
new allocation of arrays. Before I discuss the
details of such array allocations, let's remind ourselves briefly how
scalar (i.e. non-array) placement
new works.
The scalar version of placement
new takes a user-supplied address on which it constructs a single object. Unlike the ordinary version of the
new operator, placement
new doesn't allocate storage for the object; it merely constructs the object on the memory address you provide:
#include <new> //required for using placement new
class Widget {
public:
Widget();
virtual ~Widget
virtual void Draw();
};
char* buf=new char [sizeof (Widget)];//preallocate
Widget* widget= new(buf) Widget; //construct Widget on buf
widget->Draw(); //use Widget
To destroy
widget you first have to invoke its destructor explicitly:
widget->~Widget(); //explicit destructor invocation
Next, reclaim the raw memory like this:
delete[] buf;
Array Allocation
Allocating arrays with placement
new follows the same steps more or less, but you have to pay attention to additional nuances. Here is a step-by-step guide:
First, allocate a buffer large enough to hold an array of the desired type:
const int ARRSIZE = 15;
char * buf= new [sizeof(Widget)*ARRSIZE];
Don't be tempted to calculate the size manually; always use
sizeof to ensure that the buffer is properly aligned and has the right size.
Next, construct an array of
ARRSIZE objects on the buffer using placement
new[] :
Widget* widgets=new(buf) Widget[ARRSIZE];//construct an array
You can now use the allocated array as usual:
for (int i=0; i<ARRSIZE; i++)
{
widgets[i].Draw();
}
Make sure that your target class --
Widget in this example -- has a public default constructor. Otherwise, it would be impossible to create arrays thereof.
Destroying the Array
To destroy such an array allocated by placement
new you have to call the destructor for each element explicitly:
int i=ARRSIZE;
while (i)
widgets[--i].~Widget();
The
while -loop uses a descending order to preserve the
canonical destruction order of C++ -- the object that was constructed
last must be destroyed first. To comply with this requirement, the
element with the highest index is destroyed first.
Finally, you release the raw memory on which the array resided by calling
delete[] :
delete[] buf;
Performance Tuning
The array placement
new has a potential performance
problem: it initializes every element in the array unconditionally. If
your app deals with large arrays, this isn't the most efficient way. In
some apps only a portion of the array is actually used, and in other
apps the elements are assigned a different value immediately after
their construction. In these cases, you want to postpone, or even
completely avoid, the automatic initialization of array elements. To
avoid the initialization of placement
new arrays, follow the following steps:
As before, begin with an allocation of a raw buffer with the appropriate size. This time however, use the global operator
new instead of the
new operator:
Widget * warr=
static_cast<Widget*> (::operator new ( sizeof(Widget)* ARRSIZE));
The global operator
new , very much like C's
malloc() , merely allocates raw bytes of memory from the free-store, without initializing them. It returns
void * rather than
Widget* which is why you need to cast the result explicitly.
At this stage,
warr is a pointer to raw memory. You can't
access its elements because they haven't been initialized yet. To
initialize individual elements, call placement
new once more, for each element you want initialized:
void assign(Widget arr[], size_t & sz, const Widget& init)
{
new (&arr[sz++]) Widget (init); //invoke copy ctor
}
assign() passes the address of an individual element to placement
new which in turn invokes
Widget 's copy constructor. The copy-constructor initializes that element with
init . Using this technique, you can initialize elements selectively, leaving the rest of the array uninitialized.
To destroy such an array, invoke the destructor of every initialized object. Then call the global operator
delete to reclaim the raw storage:
void destroy(Widget arr[], size_t & sz)
{
while (sz)
{
arr[--sz].~Widget();//destroy all initialized elements
}
::operator delete (arr); //reclaim raw storage
}
Summary
The techniques I've presented here are bug prone. Therefore, they
should be encapsulated in higher-level classes that hide the
implementation details from users. These techniques aren't rarely-used
as they might seem. STL allocators use them under the hood to avoid
object initialization and minimize reallocations.