Orden de las llamadas al constructor y al destructor de miembros

120

Oh gurús de C ++, busco tu sabiduría. Háblame estándar y dime si C ++ garantiza que el siguiente programa:

#include <iostream>
using namespace std;

struct A
{
    A() { cout << "A::A" << endl; }
    ~A() { cout << "A::~" << endl; }
};

struct B
{
    B() { cout << "B::B" << endl; }
    ~B() { cout << "B::~" << endl; }
};

struct C
{
    C() { cout << "C::C" << endl; }
    ~C() { cout << "C::~" << endl; }
};

struct Aggregate
{
    A a;
    B b;
    C c;
};

int main()
{
    Aggregate a;
    return 0;
}

siempre producirá

A::A
B::B
C::C
C::~
B::~
A::~

En otras palabras, ¿se garantiza que los miembros se inicialicen por orden de declaración y se destruyan en orden inverso?

sbk
fuente
8
Esta es una causa razonablemente común de errores sutiles cuando las clases se han vuelto grandes y poco agradables. Cuando tiene 50 miembros de datos, y muchos de ellos inicializados en la lista de inicializadores del constructor, puede ser fácil asumir que el orden de construcción es el orden en la lista de inicializadores. Después de todo, los escritores de código han ordenado la lista con cuidado ... ¿no es así?
Permaquid

Respuestas:

139

En otras palabras, ¿se garantiza que los miembros se inicialicen por orden de declaración y se destruyan en orden inverso?

Sí a ambos. Ver 12.6.2

6 La inicialización procederá en el siguiente orden:

  • Primero, y solo para el constructor de la clase más derivada como se describe a continuación, las clases base virtuales se inicializarán en el orden en que aparecen en un recorrido en profundidad de izquierda a derecha del gráfico acíclico dirigido de clases base, donde “left -to-right ”es el orden de aparición de los nombres de clase base en la lista de especificador-base de la clase derivada.

  • Luego, las clases base directas se inicializarán en el orden de declaración tal como aparecen en la lista de especificadores de base (independientemente del orden de los inicializadores de mem).

  • Luego, los miembros de datos no estáticos se inicializarán en el orden en que fueron declarados en la definición de clase (nuevamente, independientemente del orden de los inicializadores de mem).

  • Finalmente, se ejecuta la sentencia compuesta del cuerpo del constructor. [Nota: el orden de declaración es obligatorio para garantizar que los subobjetos base y miembro se destruyan en el orden inverso de inicialización. —Nota final]

dirkgently
fuente
2
Si mal no recuerdo, sí a ambos ... Piense en ello como una pila. Primero empujado, último hecho estallar. Por lo tanto, al crear una instancia de su primera instancia, se inserta en la memoria en orden de pila. Luego, el segundo se empuja, el tercero sobre el segundo y así sucesivamente. Luego, al destruir sus instancias, el programa buscará la primera para destruir, la última que se ha enviado. Pero podría estar equivocado al explicarlo de esta manera, pero es la forma en que lo aprendí al hacer C / C ++ y ASM.
Will Marcouiller
29

Sí, lo son (miembros no estáticos, es decir). Consulte 12.6.2 / 5 para la inicialización (construcción) y 12.4 / 6 para la destrucción.

Hormiga
fuente
10

Sí, el estándar garantiza que los objetos se destruyan en el orden inverso al que fueron creados. La razón es que un objeto puede usar a otro, por lo tanto depender de él. Considerar:

struct A { };

struct B {
 A &a;
 B(A& a) : a(a) { }
};

int main() {
    A a;
    B b(a);
}

Si ase destruyera antes, bentonces btendría una referencia de miembro no válida. Al destruir los objetos en el orden inverso al que fueron creados, garantizamos una destrucción correcta.

Wilhelmtell
fuente
¡Nunca consideré que esta regla se aplicara también a la orden de destrucción de los miembros del alcance!
yano
6

Si y si. El orden de destrucción es siempre opuesto al orden de construcción, para las variables de miembro.

Chris Jester-Young
fuente