Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


Ejemplo

Demostración de la invocación automática de destructor de objetos por el compilador. También se verifica la creación de objetos temporales por el mecanismo de excepciones.

#include <iostream>
using namespace std;

int count = 0;
class A {
  public:
  int x;
  A(int i = 1) { x = i; }  // L.7: constructor por defecto
  ~A() {              // destructor
    count++;
    cout << count << ".- El destructor ha sido invocado: " << x << endl;
  }
};

int main() {          // =========================
  try {
    A a;              // L.16: se instancia un objeto
    cout << "Valor de a.x: " << a.x << endl;
    A b(0);           // L.18: se instancia un objeto especial
    throw b;          // L.19: se lanza una excepción
  }
  catch (A c) {       // L.21:
    cout << "Valor de c.x: " << c.x << endl;
  }                   // L.23:
  return 0;
}

Salida (Borland C++ 5.5):

Valor de a.x: 1
1.- El destructor ha sido invocado 0
2.- El destructor ha sido invocado 0
3.- El destructor ha sido invocado 1
Valor de c.x: 0
4.- El destructor ha sido invocado 0
5.- El destructor ha sido invocado 0

Salida (MS Visual C++ 6.0):

Valor de a.x: 1
1.- El destructor ha sido invocado: 0
2.- El destructor ha sido invocado: 1
Valor de c.x: 0
3.- El destructor ha sido invocado: 0
4.- El destructor ha sido invocado: 0

Comentario

En L.16 se instancia el objeto a. Para su inicialización es invocado el constructor por defecto (definido en L.7) por lo que la variable x del objeto adopta el valor del argumento por defecto del constructor ( i = 1), lo que se pone en evidencia en la primera salida, que se produce en L.17.

A continuación, en L.18 se instancia el objeto b, pero en este caso se invoca implícitamente el constructor con cero como argumento, por lo que el miembro b.x es cero (lo que se pone en evidencia más adelante en L.22).

A continuación, en L.19 en se lanza una excepción utilizando el objeto b como "mensajero" ( 1.6.1). En este punto difiere ligeramente el comportamiento de los compiladores analizados:

  • En Borland C++ la sentencia throw, mediante el constructor-copia ( 4.11.2d4) crea un objeto temporal idéntico a b (al que llamaremos bt); por lo que en este momento existen en el ámbito del bloque try tres objetos: b; su copia bt y a. Al ejecutarse la sentencia y producirse el salto hasta el "handler" de L.21, los tres objetos salen de ámbito, por lo que son invocados sus destructores en orden inverso de su creación; primero se destruyen b y bt; por último el primer objeto creado a, lo que se evidencia en las salidas correspondientes.
  • MS Visual C++ 6.0 no crea el objeto temporal bt, por lo que solo existen dos objetos en el bloque try que son destruidos consecuentemente, también en orden inverso al de creación.

Cuando la ejecución salta al manejador de la excepción en L.21, este se comporta como una función que recibiera un objeto tipo A como argumento. Se crea un objeto c, local al bloque catch, que recibe los valores de un objeto temporal creado por el compilador (al que llamaremos ct). Este objeto temporal es en realidad una copia del objeto b lanzado por la excepción en L.19.

La línea 22 produce una salida en la que se muestra el valor c.x. A continuación, ambos objetos, el argumento c y el objeto temporal salen de ámbito, por lo que son invocados sus destructores y son destruidos, lo que se evidencia en las dos últimas salidas.

Es de notar que el destructor ha sido invocado cinco veces, lo que evidencia que han existido cinco objetos. Esta multiplicidad de objetos, producto de la actividad de los constructores por defecto y del constructor-copia, es típica del funcionamiento de los compiladores C++, que suelen utilizar con profusión objetos temporales. Es una especie de tributo de tiempo (de creación y destrucción) y de espacio (de memoria) que hay que pagar por la comodidad derivada de su utilización. En especial cuando se pasan objetos como argumentos de funciones, si bien esta proliferación de objetos puede mitigarse en cierta forma utilizando referencias ( 4.2.3) y punteros en la medida de lo posible.