What is the use of Construction of objects at predetermined locations in C++?
The following code illustrates Construction at predetermined location-
void *address = (void *) 0xBAADCAFE ;
MyClass *ptr = new (address) MyClass (/*arguments to constructor*/) ;
This eventually creates object of MyClass, at the predetermined "address". (Assuming storage pointed by address is fairly large enough to hold MyClass object).
I would like to know the use of creating objects at such predetermined locations in memory.
There are mainly two cases:
The first is when -for example in an embedded system- you have to construct an object in a given well-known place.
The second is when you want -for some reason- to manage memory in a way other than the default.
In C++, an expression like pA = new(...) A(...)
does two consecutive things:
void* operator new(size_t, ...)
function and subsequentlyA::A(...)
.Since calling new is the only way to call A::A(), adding parameters to new allows to specialize different way to manage memory. The most trivial is to "use memory already obtained by some other means".
This method is fine when the allocation and construction needs to be separated. The typical case is std::allocator
, whose purpose is allocate uninitialized memory for a given quantity, while object contruction happens later.
This happens, for example, in std::vector
, since it has to allocate a capacity
normally wider than its actual size
, and then contruct the object as they are push_back
-ed in the space that already exist.
In fact the default std::allocator implementation, when asket to allocate n object, does a return reinterpret_cast<T*>(new char[n*sizeof(T)])
, so allocating the space, but actually not constructing anything.
Admitting that std::vector stores:
T* pT; //the actual buffer
size_t sz; //the actual size
size_t cap; //the actual capacity
allocator<T> alloc;
an implemetation of push_back can be:
void vector<T>::push_back(const T& t)
{
if(sz==cap)
{
size_t ncap = cap + 1+ cap/2; //just something more than cap
T* npT = alloc.allocate(ncap);
for(size_t i=0; i<sz; ++i)
{
new(npT+i)T(pt[i]); //copy old values (may be move in C++11)
pt[i].~T(); // destroy old value, without deallocating
}
alloc.deallocate(pt,cap);
pT = npT;
cap = ncap;
// now we heve extra capacity
}
new(pT+sz)T(t); //copy the new value
++sz; //actual size grown
}
In essence, there is the need to separate the allocation (that relates to the buffer as a whole) with the construction of the elements (that has to happen in the already existent bufffer).
One scenario where placement new is useful is:
You can preallocate big buffer once and then use many placement new
operators.
This gives you better performance(You don't need reallocations everytime) and less fragmented memory (when you need small memory chunks). Typically this is what an std::vector
imlementation uses.
The downside is, You have to manually manage the allocated memory. Objects allocated by placement new
require an explicit destructor invocation once they are not needed anymore.
Given that it is always advicable to profile your application for bottle necks instead of running over to placement new for pre-optimization.
Usually you use predetermined locations in embedded or driver code, where some hardware is addressed via certain address ranges.
But in this case the storage at that address isnt used for accessing, or better it is not intended (or better dont have to be used for it, as you dont know that the new operator is doing with it), as later on the new operation is executed.
You use it as initialization value (with new not really changing it). There come two purposes to my mind: First, in case you forgot later on a new, you instantly see in the debugger your magic address (i.e. in this case 0xBAAADCAFE).
Secondly you can use in case you fiddle around with the new operator and need an init value, so you can debug it (e.g. you can see changes).
Or you have modified your new operator that it makes whatever with that magic number (e.g. you can use it for debugging, or, like mentioned above, to really indeed use memory at a specific address for certain hardware), switch between different allocation methods, ...
EDIT: To answer it in this case correct, one needs to see what the new operator really does, you should check that news source code.
This particular behaviour is useful when you know the address of a class by having a long, DWORD, DWORD_PTR or otherwise sized pointer passed as an argument to a function and need to reconstruct a copy of the class for O-O use.
Alternatively, this could also be used to create a class in pre-allocated memory or a location which you have determined is static (ie: you are linking your application with some ancient ASM libraries).
Custom allocators, realtime (no lock here), and performance.