Ordem das chamadas de construtor e destruidor de membro

120

Ó gurus C ++, eu procuro sua sabedoria. Fale padronizado para mim e diga se C ++ garante que o seguinte 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;
}

sempre produzirá

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

Em outras palavras, é garantido que os membros sejam inicializados pela ordem de declaração e destruídos na ordem reversa?

sbk
fonte
8
Essa é uma causa razoavelmente comum de bugs sutis quando as classes ficam grandes e desordenadas. Quando você tem 50 membros de dados, e muitos deles inicializados na lista do inicializador do construtor, pode ser fácil presumir que a ordem de construção é a ordem na lista do inicializador. Afinal, os criadores do código ordenaram a lista cuidadosamente ... não é?
Permaquid

Respostas:

139

Em outras palavras, é garantido que os membros sejam inicializados pela ordem de declaração e destruídos na ordem reversa?

Sim para ambos. Veja 12.6.2

6 A inicialização deve prosseguir na seguinte ordem:

  • Em primeiro lugar, e apenas para o construtor da classe mais derivada, conforme descrito abaixo, as classes de base virtuais devem ser inicializadas na ordem em que aparecem em uma travessia da esquerda para a direita em profundidade do gráfico acíclico direcionado das classes de base, onde "à esquerda -to-right ”é a ordem de aparecimento dos nomes das classes base na lista de especificadores base da classe derivada.

  • Em seguida, as classes de base diretas devem ser inicializadas na ordem de declaração conforme aparecem na lista de especificadores de base (independentemente da ordem dos inicializadores de mem).

  • Em seguida, os membros de dados não estáticos devem ser inicializados na ordem em que foram declarados na definição da classe (novamente, independentemente da ordem dos inicializadores de mem).

  • Finalmente, a instrução composta do corpo do construtor é executada. [Observação: a ordem de declaração é obrigatória para garantir que os subobjetos de base e membros sejam destruídos na ordem inversa da inicialização. —Enviar nota]

diretamente
fonte
2
Se bem me lembro, sim para ambos ... Pense nisso como uma pilha. Primeiro empurrado, último estourado. Portanto, ao instanciar sua primeira instância, ela é colocada na memória em ordem de pilha. Então, o segundo é empurrado, o terceiro sobre o segundo e assim por diante. Então, ao destruir suas instâncias, o programa deve procurar o primeiro para destruir, o último que foi empurrado. Mas posso estar errado ao explicar dessa forma, mas é a maneira que aprendi ao fazer C / C ++ e ASM.
Will Marcouiller
29

Sim, eles são (membros não estáticos). Veja 12.6.2 / 5 para inicialização (construção) e 12.4 / 6 para destruição.

Formiga
fonte
10

Sim, o padrão garante que os objetos sejam destruídos na ordem inversa em que foram criados. A razão é que um objeto pode usar outro, portanto depender dele. Considerar:

struct A { };

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

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

Se adestruísse antes b, bhaveria uma referência de membro inválida. Ao destruir os objetos na ordem inversa em que foram criados, garantimos a destruição correta.

Wilhelmtell
fonte
Na verdade, nunca considerei que essa regra também se aplica à ordem de destruição dos membros do escopo!
yano de
6

Sim e sim. A ordem de destruição é sempre oposta à ordem de construção, para variáveis ​​de membro.

Chris Jester-Young
fonte