Outros já trataram dos outros problemas, então vou apenas observar um ponto: você já quis excluir manualmente um objeto.
A resposta é sim. @DavidSchwartz deu um exemplo, mas é bastante incomum. Vou dar um exemplo que está por trás do que muitos programadores de C ++ usam o tempo todo: std::vector
(e std::deque
, embora não seja usado tanto).
Como a maioria das pessoas sabe, std::vector
alocará um bloco maior de memória quando / se você adicionar mais itens do que sua alocação atual pode conter. Quando ele faz isso, no entanto, ele tem um bloco de memória que é capaz de conter mais objetos do que atualmente no vetor.
Para gerenciar isso, o vector
que nos oculta é alocar memória bruta por meio do Allocator
objeto (o que, a menos que você especifique de outra forma, significa que ele usa ::operator new
). Então, quando você usa (por exemplo) push_back
para adicionar um item ao vector
, internamente o vetor usa a placement new
para criar um item na parte (anteriormente) não utilizada de seu espaço de memória.
Agora, o que acontece quando / se você erase
um item do vetor? Ele não pode simplesmente usar delete
- isso liberaria todo o seu bloco de memória; ele precisa destruir um objeto naquela memória sem destruir nenhum outro, ou liberar qualquer bloco de memória que ele controla (por exemplo, se você erase
5 itens de um vetor, então imediatamente push_back
5 mais itens, é garantido que o vetor não será realocado memória quando você faz isso.
Para fazer isso, o vetor destrói diretamente os objetos na memória chamando explicitamente o destruidor, não usando delete
.
Se, por acaso, outra pessoa fosse escrever um contêiner usando armazenamento contíguo mais ou menos como um vector
faz (ou alguma variante disso, como std::deque
realmente faz), você quase certamente desejaria usar a mesma técnica.
Apenas por exemplo, vamos considerar como você pode escrever código para um buffer circular.
#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
Ao contrário dos contêineres padrão, este usa operator new
e operator delete
diretamente. Para uso real, você provavelmente deseja usar uma classe alocadora, mas no momento faria mais para distrair do que contribuir (IMO, de qualquer maneira).