実際には、挿入される要素の数に最小限必要なメモリよりも多くのメモリを割り当てるあらゆる種類のデータ構造(つまり、一度に1つのノードを割り当てるリンク構造以外のもの)を実装する必要があります。
以下のようなコンテナを取るunordered_map
、vector
またはdeque
。これらはすべて、1回の挿入ごとにヒープ割り当てを必要としないようにするために、これまでに挿入した要素に最低限必要なメモリよりも多くのメモリを割り当てます。vector
最も単純な例として使用してみましょう。
あなたがするとき:
vector<Foo> vec;
// Allocate memory for a thousand Foos:
vec.reserve(1000);
...実際に1000のFoosを構築するわけではありません。単にメモリを割り当てる/予約するだけです。vector
ここで新しい配置を使用しなかった場合、それはFoos
場所全体にデフォルトで構築され、最初の場所に挿入したことのない要素に対してもデストラクタを呼び出す必要があります。
割り当て!=構築、解放!=破壊
上記のような多くのデータ構造を実装するために一般的に言えば、メモリの割り当てと要素の構築を1つの不可分なものとして扱うことはできません。同様に、メモリの解放と要素の破壊を1つの不可分なものとして扱うこともできません。
コンストラクタとデストラクタを不必要に左右に呼び出さないようにするには、これらのアイデアを分離する必要があります。そのため、標準ライブラリはstd::allocator
(メモリの割り当て/解放時に要素を構築または破棄しない*)というアイデアをそれを使用するコンテナは、配置newを使用して要素を手動で構築し、デストラクタの明示的な呼び出しを使用して要素を手動で破棄します。
- 私はデザインが嫌いです
std::allocator
が、それは私が怒って避けるのは別の主題です。:-D
とにかく、既存のものでは構築できない汎用の標準準拠のC ++コンテナーを多数作成したので、とにかく私はそれを頻繁に使用する傾向があります。その中に含まれているのは、一般的なケースでのヒープ割り当てを回避するために数十年前に作成した小さなベクトル実装と、メモリ効率の良いトライ(一度に1つのノードを割り当てない)です。どちらの場合も、既存のコンテナーを使用してそれらを実際に実装することはできなかったため、placement new
左と右の不要なものに対してコンストラクターとデストラクターを過剰に呼び出すことを避けるために使用する必要がありました。
もちろん、カスタムリストを使用してオブジェクトを個別に割り当てる場合(フリーリストなど)、通常placement new
は次のようにを使用することもできます(例外安全性やRAIIを気にしない基本的な例):
Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);