1

Do not ask me what I'm trying to do, this is just a quick test and its only purpose is to see if there is something wrong with placement new.

I've found an issue, or I just misunderstood something.

#include <vector>

using namespace std;

#define WORKS 
int main(int argc, char** argv) {
    vector<int>* pp = (vector<int>*)malloc(sizeof(vector<int>)*20);

#ifdef WORKS
    for(int i = 0; i < 20; ++i)
    new (pp+i) vector<int>;
#else
    new (pp) vector<int>[20];
#endif

    for(int i = 0; i < 20; ++i) 
        pp[i].~vector<int>();

}

when you remove the "#define WORKS" it will give you access violation, like

    for(int i = 0; i < 20; ++i)
    new (pp+i) vector<int>;

which works good, was different from

    new (pp) vector<int>[20];

which is the cause of throwing exceptions at the destruction stage. What's going on here? I'm working on Windows XP and building with VC++ Express 2010.

Pythagoras of Samos
  • 3,051
  • 5
  • 29
  • 51
  • 4
    Whatever you're trying to do, you're doing it wrong. First of all, why are you using pointers with a vector? I don't think that's necessary. Also, don't use malloc in C++, use new. – Andrew Rasmussen Apr 19 '11 at 19:24
  • 1
    @arasmussen: Maybe he is doing it just for the sake of exploration and learning, not that he is going to write this in production code. – Nawaz Apr 19 '11 at 19:28
  • 1
    @arasmussen: So the question remains unanswered: why it throws exception? – Nawaz Apr 19 '11 at 19:29
  • 2
    Because it's wrong? That's like asking why your spellchecker tells you a word is spelled incorrectly when indeed, it is spelled incorrectly. – Brian Roach Apr 19 '11 at 19:38
  • @Brian Nope, that's like asking what is wrong with the word so it is considered incorrect. – Pythagoras of Samos Apr 19 '11 at 19:41
  • Looks like you are trying to allocate a 2-dimensional array of integers. Please see this thread: http://stackoverflow.com/questions/3755370/how-can-to-allocate-a-matrix-using-vector-on-the-heap – yasouser Apr 19 '11 at 19:47
  • For whatever it is worth, your program (once I added `#include `) compiles and runs perfectly on g++ 4.4.3 on Ubuntu 10.04.2. Also, substituting a custom class that prints during construction and destruction indicates that `new (pp) T[20]` does exactly what you think it does. – Robᵩ Apr 19 '11 at 19:47
  • Where does it crash? At allocation or deallocation? – CB Bailey Apr 19 '11 at 19:58
  • @Charles It crashes when I call those destructors in the loop. – Pythagoras of Samos Apr 19 '11 at 20:04
  • Have you tried something like: `typedef vector xxx[20]; (xxx*)pp->~xxx();` instead of the for loop when you construct the array in a single hit? – CB Bailey Apr 19 '11 at 20:09
  • Have a look here: http://stackoverflow.com/questions/15254/can-placement-new-for-arrays-be-used-in-a-portable-way ("Can placement new for arrays be used in a portable way?") – Paul Groke Apr 19 '11 at 21:01

3 Answers3

10

§5.3.4/12:

-- new T[5] results in a call of operator new[](sizeof(T)*5+x)

[ ... ]

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. [ emphasis added ]

To summarize, trying to place the array may require some unspecified amount of overhead that you're not allocating. As long as you place the elements individually, no such overhead is allowed, so the placement new works.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
1

The result of a new expression does not have to be at the same address passed to the placement new operator. And, you are not guaranteed that the size required to allocate an array is strictly the size of a single element times the number of elements.

5.3.4:

A new-expression passes the amount of space requested to the allocation function as the first argument of type std::size_t. That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array.

So, the more correct version of your code would be:

     void *ppstorage= malloc(sizeof(vector<int>)*20);
    pp= new (ppstorage) vector<int>[20];

    for(int i = 0; i < 20; ++i) 
        pp[i].~vector<int>();

Although you will almost certainly write past the end of ppstorage. The compiler has to store the count of the array somewhere to properly destruct each element, and for MSVC that is stored before the address returned by the new expression.

In theory, you could overload operator new[] to get the actual allocation size of an array:

void *operator new[](size_t *allocation_size, size_t size)
{
    *allocation_size= size;
    return nullptr;
}

But I have never tried this.

MSN
  • 53,214
  • 7
  • 75
  • 105
  • 1
    " `operator new[](void *, size_t)` does not have to return the exact same pointer passed in." Ummm, yes it does. The standard guarantees it (18.4.1.3): "**Returns:** `ptr`". – CB Bailey Apr 19 '11 at 19:49
  • would `sizeof(vector[20])` work to get the required allocation size? – Mat Apr 19 '11 at 19:51
  • 1
    Also, the size of an array is guaranteed to be the number of elements times the size of one element. Arrays must be contiguous and the pointer arithmetic rules guarantee that any padding required to make an array work must be included in the size of a single object. The array form of a new expression may pass more that the size of the requested array to `operator new[]` but that is a different issue. – CB Bailey Apr 19 '11 at 19:54
  • @Charles, you are right in that `operator new[]` must return the same address. However, the compiler does not have to return the address returned by `operator new[]` as the result of a new expression. Running the code in VS2010 verifies this. But I updated the answer to better reflect what is going on. – MSN Apr 19 '11 at 20:37
  • @Mat, why bother trying to figure out what the compiler will do when you can ask the compiler directly? – MSN Apr 19 '11 at 20:39
0

When you use operator new[] you must deallocate with operator delete[]. You cannot allocate with new[] and then deallocate one by one. So instead of your deallocation loop, you would do this:

delete [] pp;
RichN
  • 372
  • 1
  • 8