Otros ya han abordado los otros problemas, por lo que solo miraré un punto: ¿alguna vez desea eliminar manualmente un objeto?
La respuesta es sí. @DavidSchwartz dio un ejemplo, pero es bastante inusual. Daré un ejemplo que está bajo el capó de lo que muchos programadores de C ++ usan todo el tiempo: std::vector(y std::deque, aunque no se usa tanto).
Como la mayoría de la gente sabe, std::vectorasignará un bloque de memoria más grande cuando / si agrega más elementos de los que puede contener su asignación actual. Sin embargo, cuando hace esto, tiene un bloque de memoria que es capaz de contener más objetos de los que hay actualmente en el vector.
Para administrar eso, lo que vectorhace bajo las sábanas es asignar memoria sin procesar a través del Allocatorobjeto (que, a menos que especifique lo contrario, significa que usa ::operator new). Luego, cuando usa (por ejemplo) push_backpara agregar un elemento al vector, internamente el vector usa a placement newpara crear un elemento en la parte (previamente) no utilizada de su espacio de memoria.
Ahora, ¿qué sucede cuando / si tienes eraseun elemento del vector? No puede simplemente usar delete, eso liberaría todo su bloque de memoria; necesita destruir un objeto en esa memoria sin destruir ningún otro, o liberar ninguno de los bloques de memoria que controla (por ejemplo, si usted erase5 elementos de un vector, luego inmediatamente push_back5 elementos más, se garantiza que el vector no se reasignará memoria cuando lo hagas.
Para hacer eso, el vector destruye directamente los objetos en la memoria llamando explícitamente al destructor, no usando delete.
Si, por casualidad, alguien más escribiera un contenedor usando almacenamiento contiguo más o menos como lo vectorhace (o alguna variante de eso, como std::dequerealmente lo hace), es casi seguro que querrá usar la misma técnica.
Por ejemplo, consideremos cómo podría escribir código para un búfer circular 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
A diferencia de los contenedores estándar, este usa operator newy operator deletedirectamente. Para uso real, probablemente quieras usar una clase de asignador, pero por el momento haría más para distraer que para contribuir (en mi opinión, de todos modos).