ProgrammationArchitecte système C++ / Ingénieur logiciel senior

Quel est l'ordre d'initialisation des objets globaux et statiques en C++ ? Comment peut-on garantir une initialisation correcte entre différentes unités de traduction ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

En C++, les objets globaux et statiques sont initialisés avant l'entrée dans la fonction main(). Cependant, la norme ne garantit pas l'ordre d'initialisation des objets dans différentes unités de traduction (fichiers de compilation). Cela peut conduire à ce que l'on appelle le "static initialization order fiasco" — un bug où un objet global fait référence à un autre qui n'a pas encore été initialisé.

Pour garantir une initialisation correcte et éviter le problème, on utilise le patron "construct on first use" : on déclare une fonction qui retourne une référence à un objet statique local (il est créé à la première utilisation de manière sûre pour les threads à partir de C++11).

Exemple de code (patron construct on first use) :

// foo.h class Config { public: int value; }; Config& getConfig() { static Config config; config.value = 42; // initialisation garantie ! return config; }

Question piège.

Que se passe-t-il si dans deux fichiers .cpp différents, des objets globaux s'initialisent l'un l'autre ?

Réponse : Le résultat est indéfini, il peut y avoir un accès à un objet non initialisé. La solution consiste à remplacer l'initialisation globale directe par une initialisation paresseuse via un objet statique local à l'intérieur de la fonction (voir ci-dessus).


Histoire

-Dans un système CRM, deux modules ont déclaré des loggers globaux qui se référençaient l'un l'autre lors de leur initialisation. Dans différentes builds de l'application, l'ordre d'initialisation changeait, entraînant des bugs difficiles à détecter : des plantages et des journalisations sur un pointeur nul.


Histoire

-Dans un moteur graphique, un objet global conteneur pour les ressources faisait référence au gestionnaire de ressources, qui était également global. Sur Linux et Windows, les compilateurs implémentaient l'ordre de chargement différemment, ce qui entraînait une désynchronisation et divers échecs.


Histoire

-Un développeur a introduit l'initialisation globale des paramètres, puis un collègue a ajouté une autre variable globale dépendant des paramètres. Cela fonctionnait localement et échouait sur CI, où la composition des modules était différente.