В C++ глобальные и статические объекты инициализируются до входа в функцию main(). Однако стандарт не гарантирует порядок инициализации объектов, находящихся в разных единицах трансляции (файлах компиляции, translation units). Это может приводить к так называемой "static initialization order fiasco" — багу, когда один глобальный объект ссылается на другой, а тот ещё не был инициализирован.
Чтобы гарантировать корректную инициализацию и избежать проблемы, используют паттерн "construct on first use": объявляют функцию, возвращающую ссылку на статический локальный объект (он создаётся при первом вызове потоко-безопасно с C++11).
// foo.h class Config { public: int value; }; Config& getConfig() { static Config config; config.value = 42; // инициализация гарантирована! return config; }
Если в двух разных .cpp-файлах глобальные объекты создают друг друга, что произойдёт?
Ответ: Результат неопределён, может произойти обращение к неинициализированному объекту. Решение — заменить прямую глобальную инициализацию на ленивую через статик-локальный объект внутри функции (см. выше).
История
-В CRM-системе два модуля объявили глобальные логгеры, которые при инициализации ссылались друг на друга. В разных сборках приложения порядок инициализации менялся, появлялись трудноуловимые баги: падения и логгирование в нулевой указатель.
История
-В графическом движке глобальный объект-контейнер для ресурсов ссылался на менеджер ресурсов, который сам был глобальным. На Linux и Windows компиляторы реализовывали порядок загрузки по-разному, получалась рассинхронизация и разные сбои.
История
-Разработчик ввёл глобальную инициализацию настроек, а позже коллега добавил ещё одну глобальную переменную, зависящую от настроек. Это работало локально и ломалось на CI, где происходила другая компоновка модулей.