News:

This week IPhone 15 Pro winner is karn
You can be too a winner! Become the top poster of the week and win valuable prizes.  More details are You are not allowed to view links. Register or Login 

Main Menu

c++ arrays tips - Arrays, allocation, and efficiency

Started by ben2ong2, October 06, 2006, 05:55:49 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

ben2ong2

[PL] = Paul Lucas, AT&T, 11 Feb 94
[JA] = Jamshid Afshar


[PL]

Someone else wrote the code:

   #include <stddef.h>
   void * operator new (size_t, void *p) {return p;}  // in ANSI <new.h>

   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;
   }

You are not allowed to view links. Register or Login
You are not allowed to view links. Register or Login