Ordine di esecuzione nella lista di inizializzazione del costruttore

L’ordine di esecuzione nella lista di inizializzazione del costruttore è determinabile? So che l’ordine dei membri in una class è l’ordine in cui tali membri verranno inizializzati, ma se ho uno scenario come questo:

class X() { X_Implementation* impl_; }; and then providing that allocator is available: X::X():impl_(Allocate(sizeof(X_Implementation)))//HERE I'M ALLOCATING <--1 ,impl_(Construct(impl_))//AND HERE I'M CONSTRUCTING <--2 { } 

ma affinché questo sia affidabile, questo ordine DEVE essere da sinistra a destra. È garantito da GREAT BOOK OF std :: o no? Altrimenti posso sempre spostare la seconda linea nel corpo.

Secondo ISO / IEC 14882: 2003 (E) sezione 12.6.2:

L’inizializzazione deve procedere nel seguente ordine:

  • Innanzitutto, e solo per il costruttore della class più derivata come descritto di seguito, le classi di base virtuali devono essere inizializzate nell’ordine in cui appaiono su una traversata da sinistra a destra di profondità del grafo aciclico diretto delle classi di base, dove “sinistra” -to-right “è l’ordine di comparsa dei nomi delle classi di base nella class derivata-specifier-list derivata.
  • Quindi, le classi di base dirette devono essere inizializzate in ordine di dichiarazione come appaiono nella lista degli specificatori di base (indipendentemente dall’ordine degli inizializzatori di mem).
  • Quindi, i membri di dati non statici devono essere inizializzati nell’ordine in cui sono stati dichiarati nella definizione di class (sempre indipendentemente dall’ordine degli inizializzatori di mem).
  • Infine, viene eseguito il corpo del costruttore.

Quindi, segui questo ordine e avrai il tuo ordine. Inoltre, secondo lo standard, l’ordine è prescritto in modo tale che gli oggetti possano essere non inizializzati nell’ordine inverso con precisione.

Lo standard C ++ garantisce un ordine per gli elenchi di inizializzazione (ISO C ++ Standard 12.6.2 / 5):

… i membri di dati non statici devono essere inizializzati nell’ordine in cui sono stati dichiarati nella definizione di class (sempre indipendentemente dall’ordine degli inizializzatori di mem).

(Vedi la risposta di Wyatt Anderson per maggiori informazioni).

Esempio:

 class Foo { public: Foo(); private: A a; B b; C c; }; Foo::Foo() : b(), a(), c() { // a is initialized first, then b, then c - NOT b, a, then c! } 

Tuttavia, non puoi inizializzare una variabile due volte: ciò che hai non verrà compilato.

 class X //() what's with the pair of parentheses you have in your code snippet? { public: X(); private: X_Implementation* impl_; }; X::X() : impl_(Allocate(sizeof(X_Implementation))), // It is not allowed to initialize a data member twice! impl_(Construct(impl_)) { } 

Invece, metti il ​​lavoro extra nel costruttore:

 X::X() : impl_(Allocate(sizeof(X_Implementation))) { impl_ = Construct(impl_); } 

Potrebbero esserci problemi di sicurezza con il codice precedente, ma senza sapere cosa sia Allocate() o Construct() realtà non sono in grado di dirlo. Posso dirvi che è meglio separare allocazione e costruzione nelle proprie classi se lo si fa, usando l’idioma RAI (Resource Acquisition Is Initialization) :

 class XBase { protected: XBase() : impl_(Allocate(sizeof(X_Implementation))) { } ~XBase() { if(impl_ != 0) { Deallocate(impl_); } // Or something like this } X_Implementation* impl_; }; class X : private XBase // XBase is an implementation detail { public: X() { impl_ = Construct(impl_); } ~X() { Destruct(impl_); // Or something like this } }; 

In questo modo, se Construct() genera un’eccezione, non si verificherà alcuna perdita di memoria dal momento che verrà chiamato il distruttore della class base che impl_ la memoria puntata da impl_ . Questo è importante perché se l’eccezione non viene catturata e lascia il costruttore, il suo distruttore corrispondente non verrà chiamato . Vedi il documento di Bjarne Stroustrup sulla sicurezza delle eccezioni: http://www2.research.att.com/~bs/except.pdf

Il tuo scenario specifico si basa sull’idea di inizializzare lo stesso membro più di una volta. Questo è assolutamente illegale in C ++. Il tuo codice non verrà compilato. Quindi, la domanda che stai facendo non esiste davvero.

L’ordine di inizializzazione del membro è l’ordine della loro dichiarazione nella definizione della class. In contesti di non ereditarietà che coprono tutto ciò che riguarda l’ordine di inizializzazione nella lista di inizializzazione delle costruzioni.