Інші вже вирішували інші проблеми, тому я просто погляну на один момент: чи хочете ви коли-небудь видалити об'єкт вручну.
Відповідь - так. @DavidSchwartz наводив один приклад, але це досить незвичний. Наведу приклад, який знаходиться під кришкою того, що багато C ++ програмісти використовують весь час: std::vector
(і std::deque
, хоча він використовується не так сильно).
Як більшість людей знає, std::vector
виділити більший блок пам’яті, коли / якщо ви додасте більше елементів, ніж може посісти її поточний розподіл. Однак це робить блок пам'яті, здатний вмістити більше об'єктів, ніж зараз у векторі.
Для того, щоб керувати цим, те, що vector
робиться під кришками, - виділити необмежену пам'ять через Allocator
об'єкт (який, якщо не вказано інше, означає, що він використовує ::operator new
). Потім, коли ви використовуєте (наприклад) push_back
для додання елемента до vector
, внутрішньо вектор використовує a placement new
для створення елемента у (раніше) невикористаній частині його простору пам'яті.
Тепер, що станеться, коли / якщо ви erase
маєте предмет з вектора? Він не може просто використовувати delete
- це звільнить весь його блок пам'яті; йому потрібно знищити один об'єкт у цій пам'яті, не руйнуючи жодних інших блоків або звільняючи будь-який блок пам'яті, яким він керує (наприклад, якщо ви erase
5 елементів з вектора, то негайно ще push_back
5 об'єктів, це гарантовано, що вектор не перерозподіляє пам'ять, коли ви це робите.
Для цього вектор безпосередньо руйнує об'єкти в пам'яті, явно викликаючи деструктор, а не використовуючи delete
.
Якщо, можливо, хтось інший повинен був написати контейнер, використовуючи суміжне сховище приблизно так, як це vector
робиться (або якийсь варіант цього, як std::deque
насправді), ви майже напевно хочете використовувати ту саму техніку.
Для прикладу, давайте розглянемо, як ви можете написати код для кругового буфера кільця.
#ifndef CBUFFER_H_INC
#define CBUFFER_H_INC
template <class T>
class circular_buffer {
T *data;
unsigned read_pos;
unsigned write_pos;
unsigned in_use;
const unsigned capacity;
public:
circular_buffer(unsigned size) :
data((T *)operator new(size * sizeof(T))),
read_pos(0),
write_pos(0),
in_use(0),
capacity(size)
{}
void push(T const &t) {
// ensure there's room in buffer:
if (in_use == capacity)
pop();
// construct copy of object in-place into buffer
new(&data[write_pos++]) T(t);
// keep pointer in bounds.
write_pos %= capacity;
++in_use;
}
// return oldest object in queue:
T front() {
return data[read_pos];
}
// remove oldest object from queue:
void pop() {
// destroy the object:
data[read_pos++].~T();
// keep pointer in bounds.
read_pos %= capacity;
--in_use;
}
~circular_buffer() {
// first destroy any content
while (in_use != 0)
pop();
// then release the buffer.
operator delete(data);
}
};
#endif
На відміну від стандартних контейнерів, тут використовується operator new
і operator delete
безпосередньо. Для реального використання ви, мабуть, хочете використовувати клас алокатора, але наразі це зробить би більше, щоб відволікти, ніж зробити внесок (IMO, у будь-якому випадку).