web-dev-qa-db-ja.com

C ++の新機能の代わりにstd :: allocatorを使用する利点は何ですか?

std::allocatorについて読みました。私の意見では、newdeleteを使用する代わりに使用する方が複雑です。

allocatorを使用して、ヒープメモリを明示的に割り当て、構築し、破棄し、最後にメモリの割り当てを解除する必要があります。では、なぜ作成されたのですか?

どのような場合に使用できますか?また、newおよびdeleteの代わりにいつ使用する必要がありますか?

59
Mugurel

_std::allocator_は標準ライブラリコンテナのデフォルトのメモリアロケーターであり、独自のアロケーターに置き換えることができます。これにより、標準コンテナがメモリを割り当てる方法を制御できます。しかし、あなたの質問は具体的には_std::allocator_に関するものではないと思いますが、むしろ_new T[N]_を使用するのではなく、メモリを割り当て、そのメモリ内のオブジェクトを構築する戦略についてです。

その理由は、_new T[N]_では、どのコンストラクターが呼び出されるかを制御できないためです。また、すべてのオブジェクトを同時に構築する必要があります。これは、たまにしか割り当てたくない_std::vector_などの目的ではひどいものです。

生のメモリアロケータを使用すると、特定の量のメモリを割り当てて、容量を決定できます。次に、ユーザーが(選択したコンストラクターを使用して)ベクトルにアイテムを追加すると、このメモリ内の所定の場所にオブジェクトを構築できます。

次に、メモリが不足すると、より多く、通常は2倍を割り当てます。 _std::vector_が_new T[N]_を使用した場合、要素を追加または削除するたびに再割り当てする必要があり、パフォーマンスが低下します。また、_std::vector_が保持できるオブジェクトのタイプに不必要な制限を課す、すべてのオブジェクトにデフォルトのコンストラクターを使用することを強制されます。

45

私の意見では、newとdeleteを使用する代わりに使用する方が複雑です。

はい。ただし、newdeleteを置き換えるものではありません。異なる目的に使用されます。

アロケーターを使用して、ヒープメモリを明示的に割り当て、構築し、破棄し、最後にメモリの割り当てを解除する必要があります。

では、なぜ作成されたのですか?

場合によっては、割り当てと構築を2つのステップに分けたいことがあります(同様に、破壊と割り当て解除を2つのステップに分けたい場合)。そうしたくない場合は、アロケーターを使用せず、代わりにnewを使用してください。

どのような場合に使用できますか?また、newおよびdeleteの代わりにいつ使用する必要がありますか?

newdeleteの動作ではなく、アロケータの動作が必要な場合は、明らかに!典型的なケースは、コンテナを実装する場合です。

次のコードを検討してください。

_std::vector<X> v;
v.reserve(4);        // (1)
v.Push_back( X{} );  // (2)
v.Push_back( X{} );  // (3)
v.clear();           // (4)
_

ここで、行(1)は4つのオブジェクトに十分なメモリを割り当てる必要がありますが、まだ構築する必要はありません。次に、行(2)および(3)は、割り当てられたメモリにオブジェクトを構築する必要があります。次に、行(4)はそれらのオブジェクトを破棄する必要がありますが、メモリの割り当てを解除しないでください。最後に、ベクターのデストラクタで、すべてのメモリの割り当てを解除できます。

したがって、ベクターはnew X()または_delete &m_data[1]_を使用してオブジェクトを作成および破棄することはできません。構築/破棄とは別に割り当て/割り当て解除を実行する必要があります。コンテナのアロケータテンプレート引数は、メモリの(デ)割り当てとオブジェクトの構築/破棄に使用するポリシーを定義し、コンテナのメモリ使用をカスタマイズできるようにします。デフォルトのポリシーは_std::allocator_タイプです。

したがって、アロケーターが必要な場合(コンテナーを使用する場合など)にアロケーターを使用し、カスタムアロケーターを提供したくないが標準のアロケーターのみが必要な場合は_std::allocator_を使用します。

newdeleteの代わりにアロケーターを使用しません。

55
Jonathan Wakely

アロケーターはSTLで非常に重要な概念です。すべてのコンテナは、引数としてアロケータを取ることができます。次に、標準のアロケータではなく、このアロケータを使用して割り当てが実行されます。

これは便利です。プールに同じサイズのオブジェクトを割り当てるため、パフォーマンスを向上させるため、またはオブジェクトが存在する必要があるメモリの特別な領域がある場合に必要になる場合があります。

割り当てと構築の手順は別々です。ベクトル( std::vector::reserve )将来の使用のためにメモリを割り当てることができるが、(まだ)オブジェクトを作成できないことが重要です。

として、固定サイズの配列を含むアロケータをクラスとして記述し、その配列を使用して標準コンテナにメモリを提供できます。次に、そのクラスのインスタンスをスタック上に配置して、プログラムの一部のヒープ割り当てを完全に回避できます。

このSO post。 で他の例を参照してください。)==

[...]いつ使用すべきか[...]

特定のニーズがあり、独自の汎用コンテナを作成するときに最も重要な場合。

9
Daniel Jour

あなたの本能は正しいです。 90%の場合、newを使用します。ただし、たとえば map データ構造のような構造に注意してください。デフォルトのテンプレート引数の1つはclass Alloc = allocator<pair<const Key,T>は、クラスがモノの新しいインスタンスを作成し、既存のインスタンスを管理する方法を定義します。このようにして、理論的に独自のアロケーターを作成し、それを既存のデータ構造に使用できます。 newdeleteは関数であり、クラスではないため、std::allocatorそれらを表現し、それらを有効なテンプレート引数にします。

6
Silvio Mayolo

std::allocatorは、開発者がメモリの割り当て方法をより細かく制御できるようにするために作成されました。多くの組み込みシステムでは、メモリにはさまざまな種類の制約があります。莫大な量はないかもしれません。また、断片化の問題を回避するために、メモリ割り当てを最小限に抑える必要があります。

アロケーターは、異なるメモリープールからの割り当ても可能にします。したがって、たとえば、小さなサイズのブロックを割り当てると、小さなブロックメモリプールからより効率的になります。

5
Thomas Matthews

newdeleteは、動的メモリにオブジェクトを作成して初期化する直接的な方法です。ただし、アロケーターは前述のフェーズを完全に制御できるため、はるかに優れています。

アロケーターを使用して、ヒープメモリを明示的に割り当て、構築し、破棄し、最後にメモリの割り当てを解除する必要があります。

実際、アロケーターは、newdeleteが同じようにうまく機能する「通常の」コードに使用されることは想定されていません。しばしばツリーとして実装されるstd::mapのようなクラスを考えてみましょう:保持されているオブジェクトが削除されるたびにリーフ全体の割り当てを解除する必要がありますか?アロケータを使用すると、そのオブジェクトを破棄できますが、メモリを保持するので、再度オブジェクトを要求する必要はありません。

さらに、newおよびdeleteでは不可能な制御のためのより最適化されたメソッドを知っている場合、特定のタイプのアロケーターを特殊化できます。

4
edmz

この理由は [〜#〜] stl [〜#〜] メンバーが開発者により多くのメモリ制御を与えるためです。これが意味することは、たとえば、新しい演算子は実際には単なる1つの操作ではないということです。最も基本的には、メモリの予約を実行し、そのスペースをオブジェクトで満たします。

特定の現実世界のケースシナリオを思いつくことはできませんが、std::allocatorなど、特定のオブジェクトの破壊がメモリ内の他のオブジェクトに影響を与える可能性がある場合。

たとえば、引数のために、各要素がメモリ内の他のオブジェクトに二重リンクされている何らかの種類のベクトルを作成し、そのベクトルの削除時に、参照を削除するためにリンクされたオブジェクトをそれ。

4
Anzurio