Ordine delle chiamate del costruttore del membro e del distruttore


120

Oh guru del C ++, cerco la tua saggezza. Parlami standardese e dimmi se C ++ garantisce che il seguente programma:

#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;
}

produrrà sempre

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

In altre parole, è garantito che i membri vengano inizializzati in base all'ordine di dichiarazione e distrutti in ordine inverso?


8
Questa è una causa ragionevolmente comune di bug sottili quando le classi sono diventate grandi e poco sane. Quando si hanno 50 membri dati e molti di essi inizializzati nell'elenco di inizializzatori del costruttore, può essere facile presumere che l'ordine di costruzione sia l'ordine nell'elenco degli inizializzatori. Dopotutto, gli autori del codice hanno ordinato attentamente l'elenco ... non è vero?
Permaquid

Risposte:


139

In altre parole, è garantito che i membri vengano inizializzati in base all'ordine di dichiarazione e distrutti in ordine inverso?

Sì ad entrambi. Vedi 12.6.2

6 L' inizializzazione procederà nel seguente ordine:

  • Innanzitutto, e solo per il costruttore della classe più derivata come descritto di seguito, le classi di base virtuali devono essere inizializzate nell'ordine in cui appaiono su un attraversamento profondo da sinistra a destra del grafo aciclico diretto delle classi di base, dove "left -a-destra "è l'ordine di visualizzazione dei nomi delle classi di base nell'elenco degli identificatori di base della classe derivata.

  • Quindi, le classi di base dirette devono essere inizializzate in ordine di dichiarazione come appaiono nell'elenco di specificatori di base (indipendentemente dall'ordine degli inizializzatori di mem).

  • Quindi, i membri dati non statici devono essere inizializzati nell'ordine in cui sono stati dichiarati nella definizione della classe (di nuovo indipendentemente dall'ordine degli inizializzatori di mem).

  • Infine, viene eseguita l'istruzione composta del corpo del costruttore. [Nota: l'ordine di dichiarazione è obbligatorio per garantire che i suboggetti di base e membro vengano distrutti nell'ordine inverso rispetto all'inizializzazione. —End nota]


2
Se ricordo bene, sì ad entrambi ... Pensalo come una pila. Primo spinto, ultimo scoppiato. Quindi, quando si crea un'istanza della prima istanza, viene inserito in memoria in ordine di stack. Quindi, il secondo viene spinto sopra, il terzo sul secondo e così via. Quindi, quando si distruggono le istanze, il programma cercherà il primo da distruggere, l'ultimo che è stato spinto. Ma potrei sbagliarmi a spiegarlo in questo modo, ma è il modo in cui l'ho imparato facendo C / C ++ e ASM.
Will Marcouiller

29

Sì, lo sono (membri non statici). Vedere 12.6.2 / 5 per l'inizializzazione (costruzione) e 12.4 / 6 per la distruzione.


10

Sì, lo standard garantisce che gli oggetti vengano distrutti nell'ordine inverso in cui sono stati creati. Il motivo è che un oggetto può usarne un altro, quindi dipende da esso. Tener conto di:

struct A { };

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

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

Se asi dovesse distruggere prima, ballora bmanterrebbe un riferimento membro non valido. Distruggendo gli oggetti nell'ordine inverso a quello in cui sono stati creati, garantiamo la corretta distruzione.


Non ho mai considerato che questa regola si applichi anche all'ordine di distruzione dei membri dell'ambito!
yano

6

Sì e sì. L'ordine di distruzione è sempre opposto all'ordine di costruzione, per le variabili membro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.