Question

Puis-je contrôler l'ordre de destruction des objets statiques? Existe-t-il un moyen d'appliquer l'ordre que je souhaite? Par exemple, spécifier de quelque manière que je souhaite qu'un certain objet soit détruit en dernier ou au moins après un autre objet statique?

Était-ce utile?

La solution

Les objets 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, c'est que deux objets définis dans la même unité de compilation seront construits dans l'ordre de définition. Tout le reste est plus ou moins aléatoire.

Autres conseils

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

Créez uniquement une unique variable statique, d'une classe ou d'une structure contenant tout ce que vous pouvez normalement créer avec des 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 choix et, plus important encore, les les détruire dans l'ordre de votre choix, 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 suit:

static Var1Type &var1(*svars.var1);

Voil & # 224; -- contrôle total. :-) Cela dit, c'est un travail supplémentaire et généralement inutile. Mais quand cela 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 objets statiques globaux dans une seule unité de traduction, l'ordre d'initialisation est de haut en bas, l'ordre de destruction est exactement inversé. L'ordre entre plusieurs unités de traduction n'est pas défini.

Si vous avez vraiment besoin d'une commande spécifique, vous devez la composer vous-même.

Les objets statiques sont détruits dans l'ordre inverse de leur construction (par exemple, le premier objet construit est détruit en dernier), et vous pouvez contrôler la séquence de construction des objets statiques à l'aide de la technique décrite dans Élément 47, & "; Assurez-vous que les objets globaux sont initialisés avant de les utiliser &"; dans le livre de Meyers Effective C ++ .

  

Par exemple, spécifier d'une manière ou d'une autre que je souhaite qu'un objet soit détruit en dernier, ou au moins après un autre objet statique?

Assurez-vous qu'il est construit avant l'autre objet statique.

  

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

J'ignorerai (par souci de simplicité) le fait qu'ils ne sont pas dans la même DLL.

Voici le paragraphe 47 de Meyers (qui compte 4 pages). En supposant que votre global soit défini dans un fichier d’en-tête comme celui-ci ...

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

... ajoutez 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 incluant GlobalA.h (par exemple, votre fichier source GlobalB.cpp définissant votre seconde variable globale) définira une instance statique de la classe InitA, qui sera construite avant tout sinon dans ce fichier source (par exemple, avant votre deuxième variable globale).

Cette classe InitA a un compteur de références statiques. Lorsque la première instance InitA est construite, ce qui est maintenant garanti avant que votre instance GlobalB ne soit construite, le constructeur InitA peut faire tout ce qui est à faire pour que l'instance globalA soit initialisée.

Il n’ya aucun moyen de le faire en C ++ standard, mais si vous avez une bonne connaissance pratique des composants internes de votre compilateur, vous pourrez probablement le réaliser.

Dans Visual C ++, les pointeurs sur les fonctions stat stat sont situés dans le segment .CRT$XI (pour init statique de type C) ou .CRT$XC (pour le init statique de type C ++). L'éditeur de liens collecte toutes les déclarations et les fusionne par ordre alphabétique. Vous pouvez contrôler l'ordre dans lequel l'initialisation statique se produit en déclarant vos objets dans le segment approprié à l'aide de

.
#pragma init_seg

Par exemple, si vous voulez que les objets 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 effectue une itération via les pointeurs de fonction stat stat, 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 d'informations

Non, vous ne pouvez pas. Ne vous fiez jamais à l’autre, la construction / destruction d’objets 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 langage simple pour contrôler réellement l'ordre de construction et de destruction, voir ici:

#include <cassert>

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 deuxième instance de la classe, mais si vous le faites, l'assertion échouera. D'après mon expérience, cela fonctionne bien.

Vous pouvez obtenir des fonctionnalités similaires avec un static std::optional<T> au lieu d'un T. Initialisez-le simplement comme vous le feriez avec une variable, utilisez-le avec indirection et détruisez-le en affectant std::nullopt (ou, pour le boost, boost::none).

C'est différent d'avoir un pointeur en ce sens qu'il a une mémoire préallouée, ce qui est, je suppose, ce que vous voulez. Par conséquent, si vous le détruisez & Amp; (peut-être beaucoup plus tard) recréez-le, votre objet aura la même adresse (que vous pouvez conserver) et vous ne payez pas le coût de l'allocation / désallocation dynamique à ce moment-là.

Utilisez boost::optional<T> si vous n'avez pas std:: / std::experimental::.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top