Фактически требуется реализовать любой тип структуры данных, которая выделяет больше памяти, чем минимально требуется для количества вставляемых элементов (т. Е. Все, кроме связанной структуры, которая выделяет один узел за раз).
Принимать контейнеры нравится 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);