Ordre d'initialisation statique C ++
-
05-07-2019 - |
Question
Lorsque j'utilise des variables statiques en C ++, je finis souvent par vouloir initialiser une variable en transmettant une autre à son constructeur. En d'autres termes, je souhaite créer des instances statiques dépendantes les unes des autres.
Dans un seul fichier .cpp ou .h, le problème ne se pose pas: les instances sont créées dans l'ordre dans lequel elles sont déclarées. Toutefois, 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 est construite. Le résultat est que la première instance est incorrectement initialisée.
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 les solutions (y compris la solution Schwarz Counter), mais je commence à douter qu'il en existe une qui fonctionne vraiment.
Une astuce consiste à manipuler 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 (), ce qui entraîne 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 devrai apprendre à vivre avec ça ...
La solution
Vous avez répondu à votre propre question. L'ordre d'initialisation statique n'est pas défini et la manière la plus élégante de le contourner (tout en continuant l'initialisation statique, c'est-à-dire de ne pas le refactoriser complètement) consiste à envelopper l'initialisation dans une fonction.
Lisez les éléments de la FAQ C ++ à partir de https://isocpp.org/wiki / faq / ctors # static-init-order
Autres conseils
Peut-être devriez-vous vous demander 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 reformuler dans une portée locale plus petite, en particulier si vous constatez que certaines variables statiques dépendent d’autres.
Mais vous avez raison, il n’ya aucun moyen de garantir un ordre d’initialisation particulier. Par conséquent, si l’on tient à cœur, conserver l’initialisation dans une fonction, comme vous l’avez mentionné, est probablement le moyen le plus simple.
En effet, cela fonctionne. Malheureusement, vous devez écrire à globalObject (). MemberFunction () au lieu de globalObject.MemberFunction (), ce qui entraîne un code client quelque peu déroutant et inélégant.
Mais le plus important, c’est que cela fonctionne et qu’il soit à l’abri des erreurs, c.-à-d. il n'est pas facile de contourner le bon usage.
L’exactitude du programme doit être votre première priorité. En outre, à mon humble avis, le () ci-dessus est purement stylistique - c'est-à-dire. complètement sans importance.
En fonction de votre plate-forme, faites attention à une initialisation trop dynamique. Le nombre de tâches de nettoyage pouvant être initialisées pour les initialiseurs dynamiques est relativement faible (voir ici ). Vous pouvez résoudre ce problème en utilisant un conteneur d'objets globaux contenant des membres différents objets globaux. Vous avez donc:
Globals & getGlobals ()
{
static Globals cache;
return cache;
}
Un seul appel à ~ Globals () permet de nettoyer tous les objets globaux de votre programme. Pour accéder à un réseau global, vous avez encore quelque chose comme:
getGlobals().configuration.memberFunction ();
Si vous le souhaitez vraiment, vous pouvez le placer dans une macro pour enregistrer un petit peu de frappe à l'aide d'une macro:
#define GLOBAL(X) getGlobals().#X
GLOBAL(object).memberFunction ();
Bien qu'il ne s'agisse que du sucre syntaxique dans votre solution initiale.
La plupart des compilateurs (éditeurs de liens) prennent en charge un moyen (non portable) de spécifier l'ordre. Par exemple, avec Visual Studio, vous pouvez utiliser le init_seg pragma pour organiser l'initialisation dans plusieurs groupes différents. Autant que je sache, il n’ya aucun moyen de garantir l’ordre AU SEIN de chaque groupe. Dans la mesure où il s’agit d’une solution non portable, vous voudrez peut-être décider si vous pouvez corriger votre conception pour qu’elle ne soit pas nécessaire, mais cette option existe déjà.
malgré l'âge de ce fil, je voudrais proposer la solution que j'ai trouvée. Comme beaucoup l'ont déjà souligné, 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 initialise à son tour le membre et fournit un accès orienté objet. Laissez-moi vous donner un exemple, en supposant que nous voulions 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 (en GCC). Attention: les objets locaux avec stockage statique ont un cycle de vie dépendant de l'implémentation. Pour plus de détails, consultez 6.7.4 dans 2 .
Le fait d’envelopper la statique dans une méthode résoudra le problème de l’ordre, mais il n’est pas thread-safe, comme d’autres l'ont déjà souligné, mais vous pouvez le faire également pour en faire un thread si cela pose 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;
}