WEBINAR:
On-Demand
Building the Right Environment to Support AI, Machine Learning and Deep Learning
Performance Refinements
Placement
new[] has a potential performance problem. It initializes every element in the array. When dealing with large arrays, this isn't efficient. In some applications, only a portion of the array is actually used, or the elements are assigned a different value immediately after construction. In such cases, you want to defer (or completely elide) the initialization of array elements. This is possible but requires additional maneuvers.
As usual, start by allocating a raw buffer with the appropriate size. This time however, use the global operator new:
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 initialization. It returns
void *, which has to be explicitly cast to a typed pointer using
static_cast.
You're probably wondering why I use the global operator new instead of:
new char[sizeof(Widget)*ARRSIZE); //works but not recommended
Although this syntax works too, it would force you to use
reinterpret_cast instead of
static_cast to convert
char * to
Widget *.
As a rule, prefer
static_cast when possible.
At this stage, warr is actually 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, like this:
void assign(Widget arr[],
size_t & sz,
const Widget& initval)
{
new (&arr[sz++]) Widget (initval); //invoke copy ctor
}
How does it work?
assign() passes the address of an array element to placement
new which invokes Widget's copy constructor, initializing that element with
initval. This method enables you to initialize elements selectively, leaving the rest of the array uninitialized. The elements within the range
arr[sz]..arr[last] aren't initialized.
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
}
As Good as new
The techniques shown here are quite dangerous. Therefore, they should be encapsulated in higher-level classes that hide the implementation details from users.
STL allocators are a good example of this design practice. Under the hood, they use similar techniques to elide object initialization, minimize reallocations, and speed up allocations.