Can Placement New For Arrays Be Used in a Portable Way

Can placement new for arrays be used in a portable way?

Personally I'd go with the option of not using placement new on the array and instead use placement new on each item in the array individually. For example:

int main(int argc, char* argv[])
{
const int NUMELEMENTS=20;

char *pBuffer = new char[NUMELEMENTS*sizeof(A)];
A *pA = (A*)pBuffer;

for(int i = 0; i < NUMELEMENTS; ++i)
{
pA[i] = new (pA + i) A();
}

printf("Buffer address: %x, Array address: %x\n", pBuffer, pA);

// dont forget to destroy!
for(int i = 0; i < NUMELEMENTS; ++i)
{
pA[i].~A();
}

delete[] pBuffer;

return 0;
}

Regardless of the method you use, make sure you manually destroy each of those items in the array before you delete pBuffer, as you could end up with leaks ;)

Note: I haven't compiled this, but I think it should work (I'm on a machine that doesn't have a C++ compiler installed). It still indicates the point :) Hope it helps in some way!


Edit:

The reason it needs to keep track of the number of elements is so that it can iterate through them when you call delete on the array and make sure the destructors are called on each of the objects. If it doesn't know how many there are it wouldn't be able to do this.

Array placement-new requires unspecified overhead in the buffer?

Update

Nicol Bolas correctly points out in the comments below that this has been fixed such that the overhead is always zero for operator new[](std::size_t, void* p).

This fix was done as a defect report in November 2019, which makes it retroactive to all versions of C++.

Original Answer

Don't use operator new[](std::size_t, void* p) unless you know a-priori the answer to this question. The answer is an implementation detail and can change with compiler/platform. Though it is typically stable for any given platform. E.g. this is something specified by the Itanium ABI.

If you don't know the answer to this question, write your own placement array new that can check this at run time:

inline
void*
operator new[](std::size_t n, void* p, std::size_t limit)
{
if (n <= limit)
std::cout << "life is good\n";
else
throw std::bad_alloc();
return p;
}

int main()
{
alignas(std::string) char buffer[100];
std::string* p = new(buffer, sizeof(buffer)) std::string[3];
}

By varying the array size and inspecting n in the example above, you can infer y for your platform. For my platform y is 1 word. The sizeof(word) varies depending on whether I'm compiling for a 32 bit or 64 bit architecture.

C++ placement new

why is it using a char array to provide memory space for the placement new?

Why not? char is the smallest type that C++ defines, and on virtually every implementation, it is one byte in size. Therefore, it makes a good type to use when you need to allocate a block of memory of a certain size.

C++ also has very specific mechanics about how arrays of char (and only char are allocated. A new char[*], for example, will not be aligned to the alignment of char. It will be aligned to the maximum normal alignment for any type. Thus, you could use it to allocate memory and then construct any type into that memory.

Also the last line in the code above is allocating memory for an array of double, how is that possible when the original memory space contains a char array?

It is not allocating anything. It is constructing an array, using the memory you have given it. That's what placement new does, it constructs an object in the memory provided.

If the placement new is using the memory space of the char array, does this mean when we allocate the double array it overwrites the char array in that memory?

Yes.

Does using non-array placement new for neighboring elements create an array?

Am I right? Can I refer to this memory as an array? If not, can this functionality be achieved somehow?

This is basically the same underlying problem that std::vector has with data() and pointed out in CWG 2182. It's undefined behavior to refer to this memory as an array, but practically speaking it needs to work because so much code relies on it working. There's no way to achieve it.


Side-node, don't call your callables lambda. They don't have to be lambdas. And also let the language help you with pointer offsetting:

auto p = static_cast<Object*>(storage);
for (int i = 0; i < size; ++i) {
construct(static_cast<void*>(p + i), i);
}

Placement new is writing more bytes than array size

placement new of array may require more place than N * sizeof(Object)

(??? as compiler has to be able to call correctly the destructor with delete[] ???).

5.3.4 [expr.new]:

new(2,f) T[5] results in a call of operator new[](sizeof(T)*5+y,2,f).

Here, x and y are non-negative unspecified values representing array allocation overhead; the result of the new-expression will be offset by this amount from the value returned by operator new[]. This overhead may be applied in all array new-expressions, including those referencing the library function operator new[](std::size_t, void*) and other placement allocation functions. The amount of overhead may vary from one invocation of new to another. —end example ]



Related Topics



Leave a reply



Submit