Como un programador C, a menudo pensará sobre lo importante de
la inicialización, pero rara vez piensa en la limpieza. Después
de todo, ¿qué hay que limpiar de un int
?
Simplemente, olvidarlo. Sin embargo, con las librerías,
«dejarlo pasar» en un objeto cuando ya no lo
necesita no es seguro. Qué ocurre si ese objeto modifica algo en
el hardware, o escribe algo en pantalla, o tiene asociado
espacio en el montículo(heap). Si simplemente pasa de él, su
objeto nunca logrará salir de este mundo. En C++, la limpieza es
tan importante como la inicialización y por eso está garantizada
por el destructor.
La sintaxis del destructor es similar a la del constructor: se usa el nombre de la clase como nombre para la función. Sin embargo, el destructor se distingue del constructor porque va precedido de una virgulilla (~). Además, el destructor nunca tiene argumentos porque la destrucción nunca necesita ninguna opción. Aquí hay una declaración de un destructor:
class Y { public: ~Y(); };
El destructor se invoca automáticamente por el compilador cuando
el objeto sale del ámbito. Puede ver dónde se invoca al constructor
por el punto de la definición del objeto, pero la única evidencia
de que el destructor fue invocado es la llave de cierre del ámbito
al que pertenece el objeto. El constructor se invoca incluso
aunque utilice goto
para saltar fuera del del
ámbito (goto
sigue existiendo en C++ por
compatibilidad con C.) Debería notar que un
goto
no-local, implementado con las funciones
setjmp
y longjmp()
de la
librería estándar de C, evitan que el destructor sea
invocado. (Eso es la especificación, incluso si su compilador no
lo implementa de esa manera. Confiar un una característica que no
está en la especificación significa que su código no será
portable).
A continuación, un ejemplo que demuestra las características de constructores y destructores que se han mostrado hasta el momento.
//: C06:Constructor1.cpp // Constructors & destructors #include <iostream> using namespace std; class Tree { int height; public: Tree(int initialHeight); // Constructor ~Tree(); // Destructor void grow(int years); void printsize(); }; Tree::Tree(int initialHeight) { height = initialHeight; } Tree::~Tree() { cout << "inside Tree destructor" << endl; printsize(); } void Tree::grow(int years) { height += years; } void Tree::printsize() { cout << "Tree height is " << height << endl; } int main() { cout << "before opening brace" << endl; { Tree t(12); cout << "after Tree creation" << endl; t.printsize(); t.grow(4); cout << "before closing brace" << endl; } cout << "after closing brace" << endl; } ///:~
Listado 6.1. C06/Constructor1.cpp
Y esta sería la salida del programa anterior:
antes de la llave de apertura después de la creación de Tree la altura del árbol es 12 antes de la llave de cierre dentro del destructor de Tree la altura del árbol es 16 después de la llave de cierre
Puede ver que el destructor se llama automáticamente al acabar el ámbito (llave de cierre) en el que está definido el objeto.