4

次のコードは、デフォルトの Foo コンストラクターを使用して 10 個の Foo オブジェクトの配列を構築します。

Foo foo[10];

しかし、私はこれをしたくありません。私は重い foo コンストラクターを持っており、後ですべての foo オブジェクトを 1 つずつ再生成し、それを foo 配列の要素に割り当てる (コピーする) ので、配列を初期化する意味がありません。単に予約したいそのためのスペースを確保し、後で要素を設定します。の場合のように

int foo[10]

foo の要素が ={} なしでは初期化されない場合。std名前空間を使用せずにこれを行うにはどうすればよいですか(stdをサポートしていないPCと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. spacewithの定義を修飾するalignas

    alignas(Foo) char space[10 * sizeof(Foo)];
    
  3. spaceの適切な特殊化の配列を作成します ( @RMFstd::aligned_storageに感謝)

    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できreserveますpush_back

配列に穴が必要な場合...アロケーターを使用して、適切なサイズで適切に配置されたメモリのチャンクを取得し、placement-newなどを使用できます...しかし、どの穴が埋められ、どの穴が埋められているかを追跡する必要がありますとにかくそうではありません。boost::optionalすでにすべてのことを行っているので、 astd::vector<boost::optional<Foo>>はうまく機能し、多くの問題を回避できます。

于 2013-10-30T09:42:23.060 に答える
2

最も簡単な方法は、std::vector

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

したがって、 type の 10 個の要素の場所がありますが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 に答える
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が、デストラクタが破棄することに注意してください。破棄する前に( を使用して)初期化する必要があります。(OPから、それが起こると思いますが、この危険性を指摘する必要があります。)datainit()

とを使用した初期化は同等ではない{ ... }ことに注意してください。のコンストラクター( ... )に基づいて決定する必要があります。Foo

Fooこの方法で 10 秒間スタック メモリを割り当てます

FooStorage buffer[10];

i-thを初期化するにはFoo:

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

i-thを使用するにはFoo、たとえば、それの method を呼び出しますdo_something

buffer[i].data.do_something();

これが基本的な考え方です。にできる改善点はたくさんありますFooStorage

于 2013-10-30T11:38:21.090 に答える
0

boost::optional<Foo>または C++14の配列std::optional<Foo>

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

?

于 2013-10-30T19:11:14.067 に答える