Другие уже рассмотрели другие проблемы, поэтому я просто остановлюсь на одном моменте: вы когда-нибудь хотели удалить объект вручную.
Ответ положительный. @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).