Puede reescribirse el ejemplo Stash
que vimos anteriormente
en el libro, haciendo uso de los operadores new
y delete
, con las
características que se han visto desde entonces. A la vista del nuevo código se
pueden repasar estas cuestiones.
Hasta este punto del libro, ninguna de las clases Stash
ni
Stack
poseerán los objetos a los que apuntan; es decir,
cuando el objeto Stash
o Stack
sale de
ámbito, no se invoca delete
para cada uno de los objetos a los que
apunta. La razón por la que eso no es posible es porque, en un intento de conseguir
más generalidad, utilizan punteros void
. Usar delete
con
punteros void
libera el bloque de memoria pero, al no existir
información de tipo, el compilador no sabe qué destructor debe invocar.
Es necesario puntualizar que, llamar a delete
con un argumento
void*
es casi con seguridad un error en el programa, a no ser que el
puntero apunte a un objeto muy simple; en particular, que no tenga un destructor.
He aquí un ejemplo ilustrativo:
//: C13:BadVoidPointerDeletion.cpp // Deleting void pointers can cause memory leaks #include <iostream> using namespace std; class Object { void* data; // Some storage const int size; const char id; public: Object(int sz, char c) : size(sz), id(c) { data = new char[size]; cout << "Constructing object " << id << ", size = " << size << endl; } ~Object() { cout << "Destructing object " << id << endl; delete []data; // OK, just releases storage, // no destructor calls are necessary } }; int main() { Object* a = new Object(40, 'a'); delete a; void* b = new Object(40, 'b'); delete b; } ///:~
Listado 13.3. C13/BadVoidPointerDeletion.cpp
La clase Object
contiene la variable
data
de tipo void*
que es inicializada para
apuntar a un objeto simple que no tiene destructor. En el destructor de
Object
se llama a delete
con este puntero, sin que
tenga consecuencias negativas puesto que lo único que se necesita aquí es liberar
la memoria.
Ahora bien, se puede ver en main()
la necesidad de que
delete
conozca el tipo del objeto al que apunta su argumento. Esta es la
salida del programa:
Construyendo objeto a, tamaño = 40 Destruyendo objeto a Construyendo objeto b, tamaño = 40
Como delete
sabe que a
es un puntero a
Object
, se lleva a cabo la llamada al destructor de
Object
, con lo que se libera el espacio asignado a
data
. En cambio, cuando se manipula un objeto usando un
void*
, como es el caso en delete b
, se libera el bloque
de Object
, pero no se efectúa la llamada a su destructor,
con lo que tampoco se liberará el espacio asignado a data
,
miembro de Object
. Probablemente no se mostrará ningún
mensaje de advertencia al compilar el programa; no hay ningún error
sintáctico. Como resultado obtenemos un programa con una silenciosa fuga de
memoria.
Cuando se tiene una fuga de memoria, se debe buscar entre todas las llamadas a
delete
para comprobar el tipo de puntero que se le pasa. Si es un
void*
, puede estar ante una de las posibles causas (Sin embargo, C++
proporciona otras muchas oportunidades para la fuga de memoria).