Altri hanno già affrontato gli altri problemi, quindi guarderò solo a un punto: vuoi mai eliminare manualmente un oggetto.
La risposta è si. @DavidSchwartz ha fornito un esempio, ma è abbastanza insolito. Darò un esempio che è sotto il cofano di ciò che molti programmatori C ++ usano tutto il tempo: std::vector(e std::deque, sebbene non sia usato così tanto).
Come la maggior parte delle persone sa, std::vectorassegnerà un blocco di memoria più grande quando / se si aggiungono più elementi di quelli che la sua allocazione corrente può contenere. Quando lo fa, tuttavia, ha un blocco di memoria che è in grado di contenere più oggetti di quanti ne siano attualmente nel vettore.
Per gestirlo, ciò che vectorfa sotto le coperte è allocare memoria grezza tramite l' Allocatoroggetto (che, a meno che non specifichi diversamente, significa che utilizza ::operator new). Quindi, quando si utilizza (ad esempio) push_backper aggiungere un elemento a vector, internamente il vettore utilizza a placement newper creare un elemento nella parte (precedentemente) inutilizzata del suo spazio di memoria.
Ora, cosa succede quando / se eraseun elemento dal vettore? Non può semplicemente usare delete- questo libererebbe il suo intero blocco di memoria; deve distruggere un oggetto in quella memoria senza distruggerne gli altri, o liberare nessuno dei blocchi di memoria che controlla (ad esempio, se si erase5 elementi da un vettore, quindi immediatamente push_backaltri 5 elementi, è garantito che il vettore non si riallocherà memoria quando lo fai.
Per fare ciò, il vettore distrugge direttamente gli oggetti nella memoria chiamando esplicitamente il distruttore, non usando delete.
Se, forse, qualcun altro dovesse scrivere un contenitore usando l'archiviazione contigua all'incirca come vectorfa un (o una sua variante, come std::dequefa in realtà), quasi certamente vorrai usare la stessa tecnica.
Solo per esempio, consideriamo come potresti scrivere codice per un ring-buffer circolare.
#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
A differenza dei contenitori standard, questo utilizza operator newe operator deletedirettamente. Per un uso reale, probabilmente vorrai usare una classe allocatore, ma per il momento farebbe più distrarre che contribuire (IMO, comunque).