Andere haben die anderen Probleme bereits angesprochen, daher werde ich nur einen Punkt betrachten: Möchten Sie jemals ein Objekt manuell löschen?
Die Antwort ist ja. @ DavidSchwartz gab ein Beispiel, aber es ist ziemlich ungewöhnlich. Ich werde ein Beispiel geben, das unter der Haube dessen steht, was viele C ++ - Programmierer ständig verwenden: std::vector
(und std::deque
obwohl es nicht ganz so häufig verwendet wird).
Wie die meisten Leute wissen, std::vector
wird ein größerer Speicherblock zugewiesen, wenn Sie mehr Elemente hinzufügen, als die aktuelle Zuordnung aufnehmen kann. In diesem Fall verfügt es jedoch über einen Speicherblock, der mehr Objekte aufnehmen kann, als sich derzeit im Vektor befinden.
Um dies zu verwalten, vector
wird unter dem Deckmantel Rohspeicher über das Allocator
Objekt zugewiesen (was, sofern Sie nichts anderes angeben, bedeutet, dass es verwendet wird ::operator new
). Wenn Sie dann (zum Beispiel) push_back
ein Element zum hinzufügen vector
, verwendet der Vektor intern a placement new
, um ein Element im (zuvor) nicht verwendeten Teil seines Speicherplatzes zu erstellen.
Was passiert nun, wenn Sie erase
einen Artikel aus dem Vektor haben? Es kann nicht einfach verwendet werden delete
- das würde seinen gesamten Speicherblock freigeben; Es muss ein Objekt in diesem Speicher zerstören, ohne andere zu zerstören oder einen der von ihm gesteuerten Speicherblöcke freizugeben (wenn Sie beispielsweise erase
5 Elemente aus einem Vektor und sofort push_back
5 weitere Elemente auswählen, wird garantiert, dass der Vektor nicht neu zugewiesen wird Erinnerung, wenn Sie dies tun.
Zu diesem Zweck zerstört der Vektor die Objekte im Speicher direkt, indem er den Destruktor explizit aufruft und nicht verwendet delete
.
Wenn vielleicht jemand anderes einen Container unter Verwendung eines zusammenhängenden Speichers ungefähr so vector
schreibt wie ein Do (oder eine Variante davon, wie es std::deque
wirklich der Fall ist), möchten Sie mit ziemlicher Sicherheit dieselbe Technik verwenden.
Betrachten wir zum Beispiel, wie Sie Code für einen kreisförmigen Ringpuffer schreiben können.
#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;
}
// release the buffer:
~circular_buffer() { operator delete(data); }
};
#endif
Im Gegensatz zu den Standard - Containern dieser Anwendungen operator new
und operator delete
direkt. Für den realen Gebrauch möchten Sie wahrscheinlich eine Allokatorklasse verwenden, aber im Moment würde sie mehr ablenken als beitragen (IMO jedenfalls).