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

送分小仙女□ 提交于 2021-02-16 13:40:09

问题


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)?


回答1:


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



回答2:


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



回答3:


If you will not leaves holes in the array, you can simply use std::vector with reserve and push_back.

If you want holes in the array... You can obtain a properly sized and properly aligned chunk of memory with some allocator and then use placement-new and so on... But you will have to keep track of which holes are filled and which aren't anyway. boost::optional does all that already, so a std::vector<boost::optional<Foo>> would serve nicely and save you a bunch of trouble.




回答4:


The most simple way is to use std::vector

std::vector<Foo> foovec;
foovec.reserve(10);

So, there is place for 10 elements of type Foo, but they are not constructed yet.

Also, you can write something like this manually, using placing-new

char* place = static_cast<char*>(::operator new(sizeof(Foo) * 10));

And then fill with placement-new operator.

Foo* f1 = new (place) Foo(...);
Foo* f2 = new (place + sizeof(Foo)) Foo(...);
//
f1->~Foo();
f2->~Foo();
::operator delete(place);



回答5:


My solution takes into consideration these constraints (implied by the OP and some comments to different answers):

  1. We can't use the STL; and
  2. We want to use stack memory (not the heap).

It also considers the alignment issues mentioned by others and, given that the OP was tagged C++11, we can use C++11.

First introduce storage for Foo that doesn't initialize it:

union FooStorage {

    Foo data;

    FooStorage() {
    }

    template <typename... Args>
    void init(Args&&... args) {
        new (&data) Foo{static_cast<Args&&>(args)...};
    }

    ~FooStorage() {
        data.~Foo();
    }
};

Notice that the constructor doesn't initialize data but the destructor destroys it. You must initialize data (using init()) before destruction. (From the OP, I assume it's going to happen but I have to point out this danger.)

Recal that initializations using { ... } and ( ... ) are not equivalent. You must make a decision based on Foo's constructors.

Allocate stack memory for 10 Foos in this way

FooStorage buffer[10];

To initialize the i-th Foo:

buffer[i].init(/* constructor arguments */);

To use the i-th Foo by, say, calling it's method do_something:

buffer[i].data.do_something();

This is the basic idea. There a lot of improvements that can be done to FooStorage.




回答6:


Array of boost::optional<Foo> or C++14 std::optional<Foo>

boost::optional<Foo> foo[10];

?



来源:https://stackoverflow.com/questions/19678634/how-can-you-reserve-space-for-an-array-without-initializing-each-element

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!