4

以下代码将使用默认的 Foo 构造函数构造 10 个 Foo 对象的数组:

Foo foo[10];

但我不想这样做,我有 foo 构造函数,稍后我将重新生成所有 foo 对象并将其分配(复制)给 foo 数组的元素,所以初始化数组没有意义,我只想保留它的空间并稍后设置它的元素。就像在

int foo[10]

当 foo 的元素没有 ={} 时不会被初始化。我如何在不使用 std 命名空间的情况下做到这一点(我将在 PC 和不支持 std 的 CUDA 上使用代码)?

4

6 回答 6

12

你可以做所有好的动态容器都会做的事情:单独的内存分配和对象构造。

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

使用自动存储时的棘手部分是使存储在适合数组元素类型对齐的地址处对齐。有几种方法可以做到这一点;以后我会详细介绍它们:

  1. std::align(感谢@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. space限定with的定义alignas

    alignas(Foo) char space[10 * sizeof(Foo)];
    
  3. 制作space一个合适的专业化数组std::aligned_storage(感谢@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);
    
于 2013-10-30T09:41:12.883 回答
5

到目前为止,最简单的方法是使用std::vector

std::vector<Foo> foo;

如果需要,您可以调用foo.reserve(10)分配内存。而如果你有C++11,你可以使用foo.emplace_back(/*args*/)直接将对象创建到数组中,无需复制。

如果您不想/不能使用std::vector,您可以手动执行:

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

然后使用放置 new构造对象:

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

但是你最终将不得不手动调用析构函数:

fooX->~Foo();

但请注意,此解决方案可能在字节数组的对齐方面存在困难。您可能更喜欢使用malloc()以确保:

unsigned char *foo = malloc(10 * sizeof(Foo));
于 2013-10-30T09:44:00.507 回答
2

最简单的方法是使用std::vector

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

因此,有 10 个 type 元素的位置Foo,但它们尚未构造。

此外,您可以手动编写类似的内容,使用placing-new

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

然后用placement-new操作符填充。

Foo* f1 = new (place) Foo(...);
Foo* f2 = new (place + sizeof(Foo)) Foo(...);
//
f1->~Foo();
f2->~Foo();
::operator delete(place);
于 2013-10-30T09:40:05.203 回答
2

如果您不会在数组中留下空洞,您可以简单地使用std::vectorwithreservepush_back

如果你想要数组中的孔......你可以使用一些分配器获得一个大小合适且正确对齐的内存块,然后使用placement-new等等......但是你必须跟踪哪些孔被填充并且反正不是。boost::optional已经完成了所有这些,因此 astd::vector<boost::optional<Foo>>会很好地服务并为您省去很多麻烦。

于 2013-10-30T09:42:23.060 回答
0

数组boost::optional<Foo>或 C++14std::optional<Foo>

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

?

于 2013-10-30T19:11:14.067 回答
0

我的解决方案考虑了这些约束(由 OP 暗示以及对不同答案的一些评论):

  1. 我们不能使用 STL;和
  2. 我们想使用堆栈内存(而不是堆)。

它还考虑了其​​他人提到的对齐问题,并且鉴于 OP 被标记为 C++11,我们可以使用 C++11。

首先引入存储,Foo因为它不会初始化它:

union FooStorage {

    Foo data;

    FooStorage() {
    }

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

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

请注意,构造函数不会初始化data,但析构函数会破坏它。您必须在销毁之前初始化data(使用)。init()(从 OP 来看,我认为它会发生,但我必须指出这种危险。)

回忆一下使用{ ... }和的初始化( ... )是不等价的。您必须根据Foo的构造函数做出决定。

这样分配栈内存Foo10s

FooStorage buffer[10];

初始化i-th Foo

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

要使用i-th Foo,比如说,调用它的方法do_something

buffer[i].data.do_something();

这是基本思想。可以对FooStorage.

于 2013-10-30T11:38:21.090 回答