web-dev-qa-db-fra.com

Ordre d'initialisation statique C ++

Lorsque j'utilise des variables statiques en C++, je finis souvent par vouloir initialiser une variable en en passant une autre à son constructeur. En d'autres termes, je veux créer des instances statiques qui dépendent les unes des autres.

Dans un seul fichier .cpp ou .h, ce n'est pas un problème: les instances seront créées dans l'ordre où elles sont déclarées. Cependant, lorsque vous souhaitez initialiser une instance statique avec une instance dans une autre unité de compilation, l'ordre semble impossible à spécifier. Le résultat est que, en fonction de la météo, il peut arriver que l'instance qui dépend d'une autre soit construite, et ce n'est qu'après que l'autre instance soit construite. Le résultat est que la première instance est initialisée incorrectement.

Quelqu'un sait-il comment s'assurer que les objets statiques sont créés dans le bon ordre? J'ai longtemps cherché une solution, en essayant toutes (y compris la solution Schwarz Counter), mais je commence à douter qu'il y en ait une qui fonctionne vraiment.

Une possibilité est l'astuce avec le membre de fonction statique:

Type& globalObject()
{
    static Type theOneAndOnlyInstance;
    return theOneAndOnlyInstance;
}

En effet, cela fonctionne. Malheureusement, vous devez écrire globalObject (). MemberFunction (), au lieu de globalObject.MemberFunction (), résultant en un code client quelque peu déroutant et inélégant.

Mise à jour: Merci pour vos réactions. Malheureusement, il semble en effet que j'ai répondu à ma propre question. Je suppose que je vais devoir apprendre à vivre avec ça ...

58
Dimitri C.

Tu as répondu à ta propre question. L'ordre d'initialisation statique n'est pas défini, et la façon la plus élégante de le contourner (tout en faisant l'initialisation statique, c'est-à-dire en ne le refactorisant pas complètement), est d'envelopper l'initialisation dans une fonction.

Lisez les éléments C++ FAQ à partir de https://isocpp.org/wiki/faq/ctors#static-init-order

58
laalto

Vous devriez peut-être reconsidérer si vous avez besoin d'autant de variables statiques globales. Bien qu'elles puissent parfois être utiles, il est souvent beaucoup plus simple de les refactoriser sur une portée locale plus petite, surtout si vous constatez que certaines variables statiques dépendent d'autres.

Mais vous avez raison, il n'y a aucun moyen de garantir un ordre d'initialisation particulier, et donc si votre cœur y est, garder l'initialisation dans une fonction, comme vous l'avez mentionné, est probablement le moyen le plus simple.

7
Jesse Beder

En effet, cela fonctionne. Malheureusement, vous devez écrire globalObject (). MemberFunction (), au lieu de globalObject.MemberFunction (), résultant en un code client quelque peu déroutant et inélégant.

Mais la chose la plus importante est que cela fonctionne, et qu'il est à l'épreuve des pannes, c'est-à-dire. il n'est pas facile de contourner l'utilisation correcte.

L'exactitude du programme doit être votre première priorité. De plus, à mon humble avis, le () ci-dessus est purement stylistique - c'est-à-dire. complètement sans importance.

En fonction de votre plateforme, faites attention à trop d'initialisation dynamique. Il y a une quantité relativement petite de nettoyage qui peut avoir lieu pour les initialiseurs dynamiques (voir ici ). Vous pouvez résoudre ce problème à l'aide d'un conteneur d'objet global qui contient des membres différents objets globaux. Vous avez donc:

Globals & getGlobals ()
{
  static Globals cache;
  return cache;
}

Il n'y a qu'un seul appel à ~ Globals () afin de nettoyer tous les objets globaux de votre programme. Pour accéder à un global, vous avez encore quelque chose comme:

getGlobals().configuration.memberFunction ();

Si vous le vouliez vraiment, vous pouvez envelopper cela dans une macro pour économiser un tout petit peu de frappe à l'aide d'une macro:

#define GLOBAL(X) getGlobals().#X
GLOBAL(object).memberFunction ();

Bien que ce ne soit que du sucre syntaxique sur votre solution initiale.

6
Richard Corden

malgré l'âge de ce fil, je voudrais proposer la solution que j'ai trouvée. Comme beaucoup l'ont souligné avant moi, C++ ne fournit aucun mécanisme pour l'ordre d'initialisation statique. Ce que je propose est d'encapsuler chaque membre statique dans une méthode statique de la classe qui à son tour initialise le membre et fournit un accès orienté objet. Permettez-moi de vous donner un exemple, en supposant que nous voulons définir la classe nommée "Math" qui, parmi les autres membres, contient "PI":

class Math {
public:
   static const float Pi() {
       static const float s_PI = 3.14f;
       return s_PI;
   }
}

s_PI sera initialisé la première fois que la méthode Pi () est invoquée (dans GCC). Attention: les objets locaux avec stockage statique ont un style de vie dépendant de l'implémentation, pour plus de détails, vérifiez 6.7.4 dans 2 .

Mot clé statique , Standard C++

3
FabioDch

La plupart des compilateurs (éditeurs de liens) prennent en charge une manière (non portable) de spécifier l'ordre. Par exemple, avec Visual Studio, vous pouvez utiliser le pragma init_seg pour organiser l'initialisation en plusieurs groupes différents. AFAIK il n'y a aucun moyen de garantir la commande DANS chaque groupe. Comme ce n'est pas portable, vous voudrez peut-être déterminer si vous pouvez corriger votre conception pour ne pas l'exiger, mais l'option est là.

3
Dolphin

Envelopper le statique dans une méthode résoudra le problème d'ordre, mais ce n'est pas sûr pour les threads comme d'autres l'ont souligné, mais vous pouvez le faire pour le rendre également thread si cela est un problème.

// File scope static pointer is thread safe and is initialized first.
static Type * theOneAndOnlyInstance = 0;

Type& globalObject()
{
    if(theOneAndOnlyInstance == 0)
    {
         // Put mutex lock here for thread safety
         theOneAndOnlyInstance = new Type();
    }

    return *theOneAndOnlyInstance;
}
1
Ray