Фактически требуется реализовать любой тип структуры данных, которая выделяет больше памяти, чем минимально необходимое для количества вставляемых элементов (т. Е. Все, кроме связанной структуры, которая выделяет один узел за раз).
Возьмите контейнеры типа unordered_map
, vector
или deque
. Все они выделяют больше памяти, чем минимально требуется для элементов, которые вы вставили до сих пор, чтобы избежать необходимости выделения кучи для каждой отдельной вставки. Давайте использовать vector
в качестве простейшего примера.
Когда вы делаете:
vector<Foo> vec;
// Allocate memory for a thousand Foos:
vec.reserve(1000);
... это на самом деле не создает тысячу фоос. Он просто выделяет / резервирует память для них. Если бы vector
не использовал размещение new здесь, это было бы созданием по умолчанию Foos
повсюду, а также вызовом их деструкторов даже для элементов, которые вы даже никогда не вставляли в первую очередь.
Распределение! = Строительство, Освобождение! = Разрушение
Говоря в общем, для реализации многих структур данных, подобных описанным выше, вы не можете рассматривать выделение памяти и конструирование элементов как одну неделимую вещь, и вы также не можете рассматривать освобождение памяти и уничтожение элементов как одну неделимую вещь.
Должно быть разделение между этими идеями, чтобы избежать ненужного вызова конструкторов и деструкторов без необходимости влево и вправо, и поэтому стандартная библиотека разделяет идею std::allocator
(которая не создает и не уничтожает элементы, когда выделяет / освобождает память *) от контейнеров, которые ее используют, которые создают элементы вручную с помощью размещения новых и уничтожают элементы вручную, используя явные вызовы деструкторов.
- Я ненавижу дизайн
std::allocator
, но это другая тема, о которой я не буду рассказывать. : -D
Так или иначе, я склонен использовать его очень часто, так как я написал несколько стандартных для C ++ контейнеров общего назначения, которые нельзя было бы построить в терминах существующих. В их числе небольшая векторная реализация, которую я создал пару десятилетий назад, чтобы избежать выделения кучи в обычных случаях, и эффективная память (не выделяет один узел за раз). В обоих случаях я не мог по-настоящему реализовать их, используя существующие контейнеры, и поэтому мне пришлось использовать placement new
, чтобы избежать лишнего вызова конструкторов и деструкторов для ненужных вещей слева и справа.
Естественно, если вы когда-либо работаете с пользовательскими распределителями для индивидуального размещения объектов, например, со свободным списком, тогда вам, как правило, также нужно использовать placement new
, как это (основной пример, который не беспокоится об исключительной безопасности или RAII ):
Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);