问题
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:
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]
Qualify the definition of
space
withalignas
alignas(Foo) char space[10 * sizeof(Foo)];
Make
space
an array of a suitable specialization ofstd::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):
- We can't use the STL; and
- 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 Foo
s 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