I'm working on some memory space with custom allocation and deletion, which are made using a malloc-like interface, that is not under my control (i.e. opaque C-style functions for "allocate n bytes" or "free an allocated ptr"). So, nothing like new
or delete
.
Now, I want to construct an array of T
's. I get the space for that with auto space_ptr = custom_alloc(n*sizeof(T))
. Now I want to do something like array-placement-new to construct the n
elements in-place. How should I do that? ... or should I just loop from 1 to n
and construct single T
's?
Note:
alignof(T)
divides sizeof(T)
). If you want to address alignment, that would be even better, but you may ignore it for simplicity.Placement new is a variation new operator in C++. Normal new operator does two things : (1) Allocates memory (2) Constructs an object in allocated memory. Placement new allows us to separate above two things. In placement new, we can pass a preallocated memory and construct an object in the passed memory.
¶ Δ Probably not. Unless you used placement new , you should simply delete the object rather than explicitly calling the destructor.
Use of the new operator signifies a request for the memory allocation on the heap. If the sufficient memory is available, it initializes the memory and returns its address to the pointer variable. The new operator should only be used if the data object should remain in memory until delete is called.
Placement deleteIt is not possible to call any placement operator delete function using a delete expression. The placement delete functions are called from placement new expressions. In particular, they are called if the constructor of the object throws an exception.
I will assume your memory is sufficiently aligned for your T
. You probably want to check that.
The next problem is exceptions. We should really write two versions, one with the possibility that constructing will cause an exception, and one without.
I'll write the exception safe version.
template<class T, class...Args>
T* construct_n_exception_safe( std::size_t n, void* here, Args&&...args ) {
auto ptr = [here](std::size_t i)->void*{
return static_cast<T*>(here)+i;
};
for( std::size_t i = 0; i < n; ++i ) {
try {
new(ptr(i)) T(args...);
} catch( ... ) {
try {
for (auto j = i; j > 0; --j) {
ptr(j-1)->~T();
}
} catch ( ... ) {
exit(-1);
}
throw;
}
}
return static_cast<T*>(here);
}
and the not exception safe version:
template<class T, class...Args>
T* construct_n_not_exception_safe( std::size_t n, void* here, Args&&...args ) {
auto ptr = [here](std::size_t i)->void*{
return static_cast<T*>(here)+i;
};
for(std::size_t i = 0; i < n; ++i) {
new (ptr(i)) T(args...);
}
return static_cast<T*>(here);
}
You can do a tag-dispatch based system to pick between them depending on if constructing T
from Args&...
throws or not. If it throws, and ->~T()
is non-trivial, use the exception-safe one.
C++17 exposes some new functions to do exactly these tasks. They probably handle corner cases mine don't.
If you are trying to emulate new[]
and delete[]
, if T
has a non-trivial dtor you'll have to embed how many T
you created in the block.
The typical way to do this is to ask for extra room for the count at the front of the block. Ie, ask for sizeof(T)*N+K
, where K
may be sizeof(std::size_t)
.
Now in your new[]
emulator, stuff N
into the first bit, and then call construct_n
on the block right after it.
In delete[]
, subtract sizeof(std::size_t)
from the passed in pointer, read N
and then destroy the objects (from right-to-left to mirror construction order).
All this will need careful try
-catch
.
If, however, ~T()
is trivial, both your emulated new[]
and delete[]
do not store that extra std::size_t
nor do they read it.
(Note that this is how to emulate new[]
and delete[]
. How exactly new[]
and delete[]
work is implementation dependant. I'm just sketching out one way you can emulate them, it may not be compatible with how they work on your system. For example, some ABIs might always store N
even if ->~T()
is trivial, or a myriad of other variations.)
As noted by OP, you might also want to check for trivial construction prior to bothering with the above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With