TITLE: Arrays, allocation, and efficiency [PL] = Paul Lucas, AT&T, 11 Feb 94 [JA] = Jamshid Afshar [PL] Someone else wrote the code: #include void * operator new (size_t, void *p) {return p;} // in ANSI class A { public: A(int); // no default ctor }; void f() { // new A[3] is impossible here as it requires a // default constructor for A A *a=(A *) new char[sizeof(A)*3]; new (&a[0]) A(1); new (&a[1]) A(2); new (&a[2]) A(3); } [PL] I can't say that a delete[] of such a vector would work as it's supposed to--I would be wary. [JA] I can definitely say that delete [] a; results undefined behavior and is extremely likely to crash. You must instead do: a[2].~A(); // destroy backwards like regular arrays a[1].~A(); a[0].~A(); delete [] (char*)a; // remember you told new it was a char array I use this technique in my vector class so that I can allocate larger chunks of memory (for quick resizing) without creating the objects until they are needed. This also allow me to easily specify different allocation and deletion functions. Since my other container classes are based on my vector class, I can use them without interfering with operator new()/delete(). For example, I can keep a list of pointers that are currently allocated so that operator delete() will give an error if you try to delete something twice. struct CAllocater { static void* alloc(size_t s) { s = (s>0) ? s :1; return malloc(s); } static void free(void* p) { ::free(p); } }; ControlledMap< void*, size_t, CAllocator > living_ptrs; // my operator new() adds the pointer to living_ptrs and // operator delete() verifies that the pointer passed is in // living_ptrs before deleting it [PL] You cannot even say that the memory returned by the 'new char[...]' will be properly aligned for an A. You might even get a bus error at the first placement new. [JA] Actually, I think you can prove that `new char[...]' must return a block of memory properly aligned to store an A. ANSI C requires malloc() to return a block of memory that is properly aligned for any object of the requested size or smaller. Since operator new() (which may be user-defined) only knows the size of the requested allocation, it must meet the same alignment requirements as malloc(). [PL] Of course, in all current implementations known to me... (Maybe this is a hole in the standard that ISO should fix.) [JA] Do you mean ISO should officially condone the above code? I don't think it's necessary. WP 5.3.3 says "The return value from the allocation function, if non-null, will be assumed to point to a block of appropriately aligned available storage for the requested size ...". Note that the compiler is NOT required to align stack and global objects based on their size. They cannot be used to store objects of a different type: char gbuf[sizeof(A)*3]; // not necessarily aligned for A main() { new (&gbuf[0]) A(1); // run-time alignment error //... char buf2[sizeof(A)*3]; // not necessarily aligned for A new (&buf2[1]) A(2); // run-time alignment error //... } I can't actually make my living_ptrs map above a global variable since there's no specified initialization order. I really implement it like: typedef ControlledMap< void*, size_t, CAllocator > PtrMap; union { // very likely to be aligned properly to store a PtrMap double d; long i; long double ld; struct S { void* p; } s; char buf[sizeof(PtrMap)]; } hack; PtrMap* living_ptrs = 0; void* operator new(size_t s) { if (!living_ptrs) living_ptrs = new (hack.buf) PtrMap; //... living_ptrs.enter(p,s); return p; }