Los destructores son funciones miembro especiales que sirven para eliminar un objeto de una determinada clase, liberando la memoria utilizada por dicho objeto.
Los destructores tienen el mismo nombre que la clase, pero con el símbolo ~ delante, no retornan ningún valor y no pueden ser heredados.
Cuando se define un destructor para una clase, éste es llamado automáticamente cuando se abandona el ámbito en el que fue definido. Esto es así salvo cuando el objeto fue creado dinámicamente con el operador new, ya que en ese caso, si es necesario eliminarlo, hay que usar el operador delete.
En general, será necesario definir un destructor cuando nuestra clase tenga datos miembro de tipo puntero, aunque esto no es una regla estricta. El destructor no puede sobrecargarse, por la sencilla razón de que no admite argumentos.
Ejemplo:
#include <iostream.h> #include <string.h> class cadena { public: cadena(); // Constructor por defecto cadena(char *c); // Constructor desde cadena c cadena(int n); // Constructor para una cadena de n caracteres cadena(const cadena &); // Constructor copia ~cadena(); // Destructor void Asignar(char *dest); char *Leer(char *c); private: char *cad; // Puntero a char: cadena de caracteres }; cadena::cadena() { cad = NULL; } cadena::cadena(char *c) { cad = new char[strlen(c)+1]; // Reserva memoria para la cadena strcpy(cad, c); // Almacena la cadena } cadena::cadena(int n) { cad = new char[n+1]; // Reserva memoria para n caracteres cad[0] = 0; // Cadena vacía } cadena::cadena(const cadena &Cad) { // Eliminamos la cadena actual: delete[] cad; // Reservamos memoria para la nueva y la almacenamos cad = new char[strlen(Cad.cad)+1]; // Reserva memoria para la cadena strcpy(cad, Cad.cad); // Almacena la cadena } cadena::~cadena() { delete[] cad; // Libera la memoria reservada a cad } void cadena::Asignar(char *dest) { // Eliminamos la cadena actual: delete[] cad; // Reservamos memoria para la nueva y la almacenamos cad = new char[strlen(dest)+1]; // Reserva memoria para la cadena strcpy(cad, dest); // Almacena la cadena } char *cadena::Leer(char *c) { strcpy(c, cad); return c; } int main(int argc, char *argv[]) { cadena Cadena1("Cadena de prueba"); cadena Cadena2(Cadena1); // Cadena2 es copia de Cadena1 cadena *Cadena3; // Cadena3 es un puntero char c[256]; Cadena1.Asignar("Otra cadena diferente"); // Modificamos Cadena1 Cadena3 = new cadena("Cadena de prueba nš 3"); // Creamos Cadena3 // Ver resultados cout << "Cadena 1: " << Cadena1.Leer(c) << endl; cout << "Cadena 2: " << Cadena2.Leer(c) << endl; cout << "Cadena 3: " << Cadena3->Leer(c) << endl; delete Cadena3; // Destruir Cadena3. // Cadena1 y Cadena2 se destruyen automáticamente return 0; }
Quiero hacer varias observaciones sobre este programa:
Por ejemplo, si declaramos Cadena2 como:
cadena Cadena2;
Y después hacemos:
Cadena2 = Cadena1;
Lo que estaríamos copiando sería el valor del puntero cad, con lo cual, ambos punteros estarían apuntando a la misma posición de memoria. Esto es desastroso, y no simplemente porque los cambios en una cadena afectan a las dos, sino porque al abandonar el programa se intenta liberar automáticamente la misma memoria dos veces. Lo que realmente pretendemos al asignar cadenas es crear una nueva cadena que sea copia de la cadena antigua. Esto es lo que hacemos con el constructor copia, y es lo que haremos más adelante, y con más elegancia, sobrecargando el operador de asignación.
© Enero de 2.001 Salvador Pozo, salvador@c-con-clase.every1.net