Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you reserve space for an array without initializing each element?

Tags:

c++

arrays

c++11

The following code will construct array of 10 Foo objects with default Foo constructor:

Foo foo[10];

but i don't want do this, i have havy foo constructor and later i will regenarate all foo objects one by one and assign (copy) it to elements of foo array, so there is no sense to initialize array, i want just reserve space for it and set it elements later. Just like in the case of

int foo[10]

when elements of foo will not be initialized without ={}. How can i do this without using std namespace(i will use code both on PC and CUDA that isn't supports std)?

like image 863
Brans Ds Avatar asked Nov 30 '22 12:11

Brans Ds


2 Answers

You can do what all good dynamic containers do: separate memory allocation and object construction.

// allocate memory
char * space[10 * sizeof(Foo)];

// make sure it's aligned for our purposes
// see comments; this isn't actually specified to work
assert(reinterpret_cast<uintptr_t>(space) % alignof(Foo) == 0);

// populate 4th and 7th slots
Foo * p = ::new (space + 3 * sizeof(Foo)) Foo('x', true, Blue);
Foo * q = ::new (space + 6 * sizeof(Foo)) Foo('x', true, Blue);

// ...

// clean up when done
q->~Foo();
p->~Foo();

The tricky part when using automatic storage is to get storage aligned at an address suitable for the alignment of the array element type. There are several ways to accomplish this; I'll elaboate on them in the future:

  1. std::align (thanks to @Simple):

    char large_space[10 * sizeof(Foo) + 100];
    std::size_t len = sizeof large_space;
    void * space_ptr = large_space;
    
    Foo * space = static_cast<Foo *>(std::align(alignof(Foo), 10 * sizeof(Foo), space, len));
    assert(space != nullptr);
    
    // construct objects in &space[i]
    
  2. Qualify the definition of space with alignas

    alignas(Foo) char space[10 * sizeof(Foo)];
    
  3. Make space an array of a suitable specialization of std::aligned_storage (thanks to @RMF)

    std::aligned_storage<sizeof(Foo), alignof(Foo)>::type space[10];
    
    Foo *p = new (&space[3]) Foo('x', true, Blue);
    Foo *q = new (&space[6]) Foo('x', true, Blue);
    
like image 180
Kerrek SB Avatar answered Jan 05 '23 00:01

Kerrek SB


The easiest way is by far to use std::vector:

std::vector<Foo> foo;

You can call foo.reserve(10) to allocate the memory, if needed. And if you have C++11, you can use foo.emplace_back(/*args*/) to create the objects directly into the array, no need to copy.

If you don't want to/ can't use std::vector, you can do it manually:

unsigned char foo[10 * sizeof(Foo)];

And then to construct the objects use the placement new:

int x = ...;
Foo *fooX = new (foo[x * sizeof(Foo)) Foo(/*args to the constructor*/);

But then you will have to call the destructors manually, eventually:

fooX->~Foo();

But note that this solution may have difficulties with the alignment of the byte array. You may prefer to use malloc() to be sure:

unsigned char *foo = malloc(10 * sizeof(Foo));
like image 41
rodrigo Avatar answered Jan 05 '23 01:01

rodrigo