2010-10-27 22 views
9

L'ordre d'exécution dans la liste d'initialisation du constructeur est-il déterminable? Je sais que les membres ordre dans une classe est l'ordre dans lequel les membres seront initialisés mais si je le scénario comme celui-ci:Ordre d'exécution dans la liste d'initialisation du constructeur

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<X_Implementation>(impl_))//AND HERE I'M CONSTRUCTING <--2 
{ 
} 

, mais pour que cela soit fiable cet ordre doit être de gauche à droite. Est-ce garanti par GREAT BOOK OF std :: ou pas? Sinon, je peux toujours déplacer la deuxième ligne dans le corps.

+1

Utilisez le placement new à construire dans un tampon mémoire que vous avez précédemment alloué. –

+0

Vous ne pouvez pas initialiser un objet plus d'une fois, cette question est donc non-séquentielle. Au-delà de cela, une copie évidente de [Ordre d'évaluation de la liste d'initialisation du constructeur] (https://stackoverflow.com/questions/1242830/constructor-initialization-list-evaluation-order) –

Répondre

28

Selon ISO/CEI 14882: 2003 (F) L'article 12.6.2:

Initialisation doit procéder dans l'ordre suivant:

  • Première , et seulement pour le constructeur de la classe la plus dérivée décrite ci-dessous, les classes de base virtuelles doivent être initialisées dans l'ordre où elles apparaissent sur une traversée gauche-droite en profondeur du graphe acyclique orienté des classes de base, où " de gauche à droite "i s l'ordre d'apparition des noms de classe de base dans la liste de spécification de base de classe dérivée .
  • Ensuite, les classes de base directes doivent être initialisées dans l'ordre de déclaration telles qu'elles apparaissent dans la liste de spécification de base (quel que soit l'ordre des initialiseurs mem).
  • Ensuite, les membres de données non statiques doivent être initialisés dans l'ordre dans lequel ils ont été déclarés dans la définition de classe (à nouveau sans tenir compte de l'ordre des mem-initialiseurs).
  • Enfin, le corps du constructeur est exécuté.

Donc, suivez cet ordre, et vous aurez votre commande. Toujours selon la norme, la commande est prescrite en tant que telle afin que les objets puissent être non initialisés dans l'ordre inverse précis.

22

La norme C de ne garantit une commande pour les listes d'initialisation (standard ISO C++ 12.6.2/5):

... membres de données non statiques doivent être initialisés dans l'ordre où ils ont été déclarés dans la définition de classe (encore une fois sans tenir compte de l'ordre des mém-initialisateurs).

(Voir Wyatt Anderson's answer pour plus d'informations.)

Exemple:

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! 
} 

Cependant, vous ne pouvez pas initialiser une variable deux fois - ce que vous avez ne compilera pas.

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<X_Implementation>(impl_)) 
{ 
} 

Au lieu de cela, il suffit de mettre le travail supplémentaire dans le constructeur:

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

Il peut y avoir des problèmes de sécurité d'exception avec le code ci-dessus, mais sans savoir ce Allocate() ou Construct() ne fait I » Je ne peux pas dire. Je peux vous dire qu'il est préférable de séparer l'allocation et la construction dans leurs propres classes si vous faites cela, en utilisant l'acquisition de ressources Est Initialisation (RAII) de langage:

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<X_Implementation>(impl_); 
    } 

    ~X() 
    { 
     Destruct<X_Implementation>(impl_); // Or something like this 
    } 
}; 

De cette façon, si Construct() jette une exception, vous ne perdrez pas de mémoire car le destructeur de classe de base sera appelé, ce qui libèrera la mémoire pointée par impl_. Ceci est important car si l'exception n'est pas interceptée et quitte le constructeur, son destructeur correspondant ne sera pas appelé. Voir le document de Bjarne Stroustrup sur exception sécurité: http://www2.research.att.com/~bs/except.pdf

4

Votre scénario spécifique est basé sur l'idée d'initialiser le même membre plus d'une fois. Ceci est clairement illégal en C++. Votre code ne sera pas compilé. Donc, la question que vous posez n'existe pas vraiment.

L'ordre d'initialisation des membres est l'ordre de leur déclaration dans la définition de classe. Dans les contextes sans héritage, cela couvre tout ce qui est lié à l'ordre d'initialisation dans la liste d'initialisation des constructions.