tags:

views:

835

answers:

7

Is it possible to actually make use of placement new in portable code when using it for arrays?

It appears that the pointer you get back from new[] is not always the same as the address you pass in (5.3.4, note 12 in the standard seems to confirm that this is correct), but I don't see how you can allocate a buffer for the array to go in if this is the case.

The following example shows the problem. Compiled with Visual Studio, this example results in memory corruption:

#include <new>
#include <stdio.h>

class A
{
public:

 A() : data(0) {}
 virtual ~A() {}
 int data;
};

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

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

 // With VC++, pA will be four bytes higher than pBuffer
 printf("Buffer address: %x, Array address: %x\n", pBuffer, pA);

 // Debug runtime will assert here due to heap corruption
 delete[] pBuffer;

 return 0;
}

Looking at the memory, the compiler seems to be using the first four bytes of the buffer to store a count of the number of items in it. This means that because the buffer is only sizeof(A)*NUMELEMENTS big, the last element in the array is written into unallocated heap.

So the question is can you find out how much additional overhead your implementation wants in order to use placement new[] safely? Ideally, I need a technique that's portable between different compilers. Note that, at least in VC's case, the overhead seems to differ for different classes. For instance, if I remove the virtual destructor in the example, the address returned from new[] is the same as the address I pass in.

+1  A: 
Yossi Kreinin
+4  A: 

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.

So are you going to accept an answer?? ;)

OJ
+2  A: 

Similar to how you would use a single element to calculate the size for one placement-new, use an array of those elements to calculate the size required for an array.

If you require the size for other calculations where the number of elements may not be known you can use sizeof(A[1]) and multiply by your required element count.

e.g

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

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

Thanks for the replies. Using placement new for each item in the array was the solution I ended up using when I ran into this (sorry, should have mentioned that in the question). I just felt that there must have been something I was missing about doing it with placement new[]. As it is, it seems like placement new[] is essentially unusable thanks to the standard allowing the compiler to add an additional unspecified overhead to the array. I don't see how you could ever use it safely and portably.

I'm not even really clear why it needs the additional data, as you wouldn't call delete[] on the array anyway, so I don't entirely see why it needs to know how many items are in it.

James Sutherland
+1  A: 

@James

I'm not even really clear why it needs the additional data, as you wouldn't call delete[] on the array anyway, so I don't entirely see why it needs to know how many items are in it.

After giving this some thought, I agree with you. There is no reason why placement new should need to store the number of elements, because there is no placement delete. Since there's no placement delete, there's no reason for placement new to store the number of elements.

I also tested this with gcc on my Mac, using a class with a destructor. On my system, placement new was not changing the pointer. This makes me wonder if this is a VC++ issue, and whether this might violate the standard (the standard doesn't specifically address this, so far as I can find).

Derek Park
+1  A: 

@Derek

5.3.4, section 12 talks about the array allocation overhead and, unless I'm misreading it, it seems to suggest to me that it is valid for the compiler to add it on placement new as well:

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.

That said, I think VC was the only compiler that gave me trouble with this, out of it, GCC, Codewarrior and ProDG. I'd have to check again to be sure, though.

James Sutherland
+3  A: 

Placement new itself is portable, but the assumptions you make about what it does with a specified block of memory are not portable. Like what was said before, if you were a compiler and were given a chunk of memory, how would you know how to allocate an array and properly destruct each element if all you had was a pointer? (See the interface of operator delete[].)

Edit:

And there actually is a placement delete, only it is only called when a constructor throws an exception while allocating an array with placement new[].

Whether new[] actually needs to keep track of the number of elements somehow is something that is left up to the standard, which leaves it up to the compiler. Unfortunately, in this case.

MSN

Mat Noguchi