3

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

I want to allocate array of object T and initialize object using object constructor. This is easy using c++ new :

T * pointerT = new T [arraySize];

It will call T constructor for all of the arraySize objects. However, for some reason I have to use C memalign instead of new. In this case I end up to use following code

T * pointerT = (T*) memalign(64,arraySize * sizeof(T)); 
new (pointerT) T(); 

new (pointerT) T() calls T constructor only one time. However, I need to call T constructor for all objects not only the first one.

I do appreciate your help.

Community
  • 1
  • 1
ARH
  • 1,355
  • 3
  • 18
  • 32

2 Answers2

8

Do new (pointerT) T() in a loop. Please keep pointerT inside an object whose destructor destroys the objects and calls free (call it eg. aligned_vector), and in the constructor, do:

ptrdiff_t k = 0;

try
{
    for (; k < n; k++)
        new (pointerT + k) T();
}

catch (...)
{
    for (; k > 0; k--) (pointerT + k)->~T();
    free(pointerT);
    throw;
}

This way, if a construction fails, you can bail out transactionally and not leak memory or resources.

For this purpose, the simplest in terms of reusability would be to implement your own alignment aware allocator and use std::vector, which takes care of exception safety (and many other goodies) for you.

Here is a sample allocator, C++11, with compile time alignment specification (please suggest enhancements and corrections):

template <typename T, size_t align>
struct aligned_allocator
{
    typedef T value_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;

    template <typename U>
    struct rebind { typedef aligned_allocator<U, align> other; };

    T* address(T& t) { return &t; }

    T* allocate(size_t n, const T* = 0) 
    {
        if (T* ans = memalign(align, n * sizeof(T))) return ans;
        else throw std::bad_alloc();
    }

    T* deallocate(T* p, size_t) { free(p); }

    size_t max_size() const 
    {
        return size_t(-align) / sizeof(T); 
    }

    template <typename U, typename... Args>
    void construct(U* p, Args&&... args)
    {
        ::new((void *)p U(std::forward<Args>(args)...);
    }

    template <typename U>
    void destroy(U* p) { p->~U(); }
};

Sample usage: std::vector<T, aligned_allocator<T, 64>> v(42); constructs a vector with aligned storage and 64 elements.

You can also do, in C++11

template <typename T, size_t align>
using aligned_vector = std::vector<T, aligned_allocator<T, align>>;

and you can now use

aligned_vector<MyType, 64> v;

and enjoy an exception safe, move aware, iterators aware, range-based-for-loop aware, etc, vector with aligned storage.

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
2

If T is copyable, something like:

std::uninitialized_fill_n( pointerT, arraySize, T()); // #include <memory>

should do the trick. It'll even handle destroying objects for you that have been constructed if an exception is thrown part way through.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760