Ordre de destruction des objects statiques en C ++

Puis-je contrôler l’ordre de destruction des objects statiques? Y a-t-il un moyen d’imposer ma commande souhaitée? Par exemple, pour spécifier d’une certaine manière que je voudrais qu’un object soit détruit en dernier, ou au moins après un autre object statique?

Les objects statiques sont détruits dans l’ordre inverse de la construction. Et l’ordre de construction est très difficile à contrôler. La seule chose dont vous pouvez être sûr est que deux objects définis dans la même unité de compilation seront construits dans l’ordre de définition. Tout le rest est plus ou moins aléatoire.

Les autres réponses à cette question insistent sur le fait que cela ne peut pas être fait. Et ils ont raison, selon les spécifications – mais il y a un truc qui vous permettra de le faire.

Créez uniquement une seule variable statique, d’une classe ou d’une structure contenant toutes les autres choses que vous créeriez normalement comme variables statiques, comme ceci:

class StaticVariables { public: StaticVariables(): pvar1(new Var1Type), pvar2(new Var2Type) { }; ~StaticVariables(); Var1Type *pvar1; Var2Type *pvar2; }; static StaticVariables svars; 

Vous pouvez créer les variables dans l’ordre de votre StaticVariables et, plus important encore, les détruire dans l’ordre de votre StaticVariables , dans le constructeur et le destructeur de StaticVariables . Pour rendre cela complètement transparent, vous pouvez également créer des références statiques aux variables, comme ceci:

 static Var1Type &var1(*svars.var1); 

Voilà – un contrôle total. 🙂 Cela dit, c’est du travail supplémentaire, et généralement inutile. Mais quand c’est nécessaire, il est très utile de le savoir.

Réponse courte: En général, non.

Réponse légèrement plus longue: Pour les objects statiques globaux dans une seule unité de traduction, l’ordre d’initialisation est de haut en bas, l’ordre de destruction est exactement inverse. L’ordre entre plusieurs unités de traduction n’est pas défini.

Si vous avez vraiment besoin d’un ordre spécifique, vous devez le faire vous-même.

Les objects statiques sont détruits dans l’ordre inverse de leur construction (par exemple, le premier object construit est détruit en dernier), et vous pouvez contrôler la séquence dans laquelle les objects statiques sont construits, en utilisant la technique décrite dans l’article 47, ” Assurez-vous que les objects globaux sont initialisés avant d’être utilisés ” dans le livre de Meyers Effective C ++ .

Par exemple, pour spécifier d’une certaine manière que je voudrais qu’un object soit détruit en dernier, ou du moins après un autre object statique?

Assurez-vous qu’il est construit avant l’autre object statique.

Comment puis-je contrôler l’ordre de construction? toutes les statiques ne sont pas dans la même DLL.

Je vais ignorer (pour simplifier) ​​le fait qu’ils ne sont pas dans la même DLL.

Ma paraphrase de l’article 47 de Meyers (qui fait 4 pages) est la suivante. En supposant que vous êtes global défini dans un fichier d’en-tête comme celui-ci …

 //GlobalA.h extern GlobalA globalA; //declare a global 

… append du code à ce fichier d’inclusion comme ceci …

 //GlobalA.h extern GlobalA globalA; //declare a global class InitA { static int refCount; public: InitA(); ~InitA(); }; static InitA initA; 

Cela aura pour effet que tout fichier qui inclut GlobalA.h (par exemple, votre fichier source GlobalB.cpp qui définit votre deuxième variable globale) définira une instance statique de la classe InitA, qui sera construite avant toute autre chose dans cette classe. fichier source (par exemple avant votre deuxième variable globale).

Cette classe InitA a un compteur de référence statique. Lorsque la première instance InitA est créée, ce qui est désormais garanti avant la construction de votre instance GlobalB, le constructeur InitA peut faire tout ce qu’il doit pour s’assurer que l’instance globalA est initialisée.

Il n’y a aucun moyen de le faire en C ++ standard, mais si vous avez une bonne connaissance pratique de vos internes de compilateur spécifiques, cela peut probablement être réalisé.

Dans Visual C ++, les pointeurs vers les fonctions d’initialisation statiques se trouvent dans le segment .CRT$XI (pour l’initialisation statique de type C) ou .CRT$XC segment .CRT$XC (pour l’initialisation statique de type C ++). Vous pouvez contrôler l’ordre dans lequel l’initialisation statique se produit en déclarant vos objects dans le segment approprié en utilisant

 #pragma init_seg 

Par exemple, si vous souhaitez que les objects du fichier A soient créés avant le fichier B:

Fichier A.cpp:

 #pragma init_seg(".CRT$XCB") class A{}A; 

Fichier B.cpp:

 #pragma init_seg(".CRT$XCC") class B{}B; 

.CRT$XCB est fusionné avant .CRT$XCC . Lorsque le CRT répète les pointeurs de la fonction d’initialisation statique, il rencontrera le fichier A avant le fichier B.

Dans Watcom, le segment est XI et les variations sur #pragma initialize peuvent contrôler la construction:

 #pragma initialize before library #pragma initialize after library #pragma initialize before user 

… voir la documentation pour plus

Lis:
Commande d’initialisation SO

SO Résoudre l’ordre des problèmes d’initialisation

Non, vous ne pouvez pas. Vous ne devez jamais compter sur l’autre de la construction / destruction d’objects statiques.

Vous pouvez toujours utiliser un singleton pour contrôler l’ordre de construction / destruction de vos ressources globales.

Avez-vous vraiment besoin que la variable soit initialisée avant main ?

Si vous ne le faites pas, vous pouvez utiliser un simple langage pour contrôler l’ordre de construction et de destruction en toute simplicité, voir ici:

 #include  class single { static single* instance; public: static single& get_instance() { assert(instance != 0); return *instance; } single() // : normal constructor here { assert(instance == 0); instance = this; } ~single() { // normal destructor here instance = 0; } }; single* single::instance = 0; int real_main(int argc, char** argv) { //real program here... //everywhere you need single::get_instance(); return 0; } int main(int argc, char** argv) { single a; // other classes made with the same pattern // since they are auto variables the order of construction // and destruction is well defined. return real_main(argc, argv); } 

Cela ne vous empêche pas d’essayer de créer une seconde instance de la classe, mais si vous le faites, l’assertion échouera. Dans mon expérience, cela fonctionne bien.

Vous pouvez effectivement obtenir une fonctionnalité similaire en ayant un static std::optional au lieu d’un T Il suffit de l’initialiser comme vous le feriez avec une variable, de l’utiliser avec indirection et de le détruire en assignant std::nullopt (ou, pour booster, boost::none ).

C’est différent d’avoir un pointeur dans le fait qu’il possède une mémoire préallouée, c’est-à-dire que je suppose ce que vous voulez. Par conséquent, si vous le détruisez et (peut-être beaucoup plus tard), recréez-le, votre object aura la même adresse (que vous pouvez conserver) et vous ne payerez pas le coût de l’allocation / désallocation dynamic à ce moment-là.

Utilisez boost::optional si vous n’avez pas std:: / std::experimental:: .