Насправді потрібна реалізація будь-якої структури даних, яка виділяє більше пам'яті, ніж мінімально потрібно для кількості вставлених елементів (тобто нічого, крім пов'язаної структури, яка виділяє один вузол за один раз).
Приймати контейнери подобається unordered_map
, vector
або deque
. Усі вони виділяють більше пам’яті, ніж потрібно мінімально для елементів, які ви вставили до цього часу, щоб уникнути необхідності виділення кучі для кожної окремої вставки. Скористаємося vector
як найпростіший приклад.
Коли ви робите:
vector<Foo> vec;
// Allocate memory for a thousand Foos:
vec.reserve(1000);
... що насправді не побудує тисячу Фоос. Він просто виділяє / резервує пам'ять для них. Якщо vector
б тут не було використано нове розміщення, було б побудовано 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);