14.3.4. Orden de llamada de constructores y destructores

Es importante conocer el orden de las llamadas de los constructores y destructores de un objeto con varios subobjetos. El siguiente ejemplo muestra cómo funciona:

//: C14:Order.cpp
// Constructor/destructor order
#include <fstream>
using namespace std;
ofstream out("order.out");

#define CLASS(ID) class ID { \
public: \
  ID(int) { out << #ID " constructor\n"; } \
  ~ID() { out << #ID " destructor\n"; } \
};

CLASS(Base1);
CLASS(Member1);
CLASS(Member2);
CLASS(Member3);
CLASS(Member4);

class Derived1 : public Base1 {
  Member1 m1;
  Member2 m2;
public:
  Derived1(int) : m2(1), m1(2), Base1(3) {
    out << "Derived1 constructor\n";
  }
  ~Derived1() {
    out << "Derived1 destructor\n";
  }
};

class Derived2 : public Derived1 {
  Member3 m3;
  Member4 m4;
public:
  Derived2() : m3(1), Derived1(2), m4(3) {
    out << "Derived2 constructor\n";
  }
  ~Derived2() {
    out << "Derived2 destructor\n";
  }
};

int main() {
  Derived2 d2;
} ///:~

Listado 14.7. C14/Order.cpp


Primero, se crea un objeto ofstream para enviar la salida a un archivo. Entonces, para no teclear tanto y demostrar la técnica de las macros que será sustituida por otra mucho más mejorada en el capítulo 16, se crea una para construir varias clases que utilizan herencia y composición. Cada constructor y destructor escribe información en el archivo de salida. Fíjense que los constructores no son constructores por defecto; cada uno tiene un parámetro del tipo int. Y además, el argumento no tiene nombre; la única razón de su existencia es forzar la llamada al constructor en la lista de inicializadores del constructor. (Eliminando el identificador evita que el compilador informe con mensajes de advertencia)

La salida de este programa es

Base1 constructor
Member1 constructor
Member2 constructor
Derived1 constructor
Member3 constructor
Member4 constructor
Derived2 constructor
Derived2 destructor
Member4 destructor
Member3 destructor
Derived1 destructor
Member2 destructor
Member1 destructor
Base1 destructor

omo puede observar, la construcción empieza desde la raíz de la jerarquía de clases y en cada nivel, el constructor de la clase base se ejecuta primero, seguido por los constructores de los objetos miembro. Los destructores son llamados exactamente en el orden inverso que los constructores -esto es importante debido a los problemas de dependencias (en el constructor de la clase derivada o en el destructor, se debe asumir que el subobjeto de la clase base esta todavía disponible para su uso, y ha sido construido - o no se ha destruido todavía).

También es interesante que el orden de las llamadas al constructor para los objetos miembro no afecten para nada el orden de las llamadas en la lista de inicializadores de un constructor. El orden es determinado por el orden en que los objetos miembros son declarados en la clase. Si usted pudiera cambiar el orden del constructor en la lista de inicializadores de un constructor, usted podría tener dos secuencias diferentes de llamada en dos constructores diferentes, pero el destructor no sabría como invertir el orden para llamarse correctamente y nos encontraríamos con problemas de dependencias.