# Static initialization order fiasco Проблемы с использованием объектов до окончания их полной инициализации наигрывается во многих языках программирования. Сомнительный дизайн с разрывом объявления, конструирования и инициализации можно воплотить в жизнь чуть ли ни где угодно. Но обычно для этого все-таки надо приложить некоторые усилия. А в C/C++ можно вляпатся незаметно, случайно и очень долго об этого не подозревать. В C/C++ мы можем разделять код программы по разным, независимым единицам трансляции (в разные .c/.cpp файлы). Они могут компилироваться параллельно. Скорость сборки повышается. И все было бы хорошо. Но только в одном «модуле» появляется глобальная переменная, используемая в другом модуле, начинаются проблемы. И проблемы не только от того, что глобальные переменные в принципе признак не самого удачного дизайна. Проблема в том, что связи между модулями нет (заголовочные файлы ничего не связывают). И после объединения модулей код с инициализацией глобальной переменной может оказаться ПОСЛЕ кода с использованием. Стандарты C и С++ гарантируют, что глобальные переменные будут сконструированы в порядке их объявления внутри единицы трансляции. А между единицами трансляции — неопределен. И вместе с порядком неопределено и поведение программы. ```C++ // module.h extern int global_value; // module.cpp #include "module.h" int init_func() { return 5 * 5; } int global_value = init_func(); // main.cpp #include "module.h" #include static int use_global = global_value * 5; int main() { std::cout << use_global; } ``` Результат будет [зависеть](https://godbolt.org/z/zvffd1) от того, в каком порядке будут обработаны `main.cpp` и `module.cpp`. До C++11 в следующем простеньком примере было неопределенное поведение. Как раз из-за возможности неправильного порядка инициализации статических объектов. ```C++ #include struct Init { Init() { std::cout << "Init!\n"; } } init; // до C++11 не было гарантии, // что std::cout сконструирован к этому моменту int main() { return 0; } ``` Бороться с неправильным порядком инициализации можно, например, организовав доступ к глобальной переменной через вызов функции. ```C++ // module.h int global_variable(); // module.cpp int global_variable() { static int glob_var = init_func(); return glob_var; } ``` В таком случае при первом же доступе инициализация гарантировано произойдет. ----------------- Помимо неопределенного поведения из-за неправильного порядка инициализации, наиграть можно проблемы и с порядком деинициализации! Стандарт C++ гарантирует, что деструкторы объектов всегда вызываются в порядке, обратном порядку завершения работы конструкторов. ```C++ #include #include const std::string& static_name() { static const std::string name = "Hello! Hello! long long string!"; return name; } struct TestStatic { TestStatic() { std::cout << "ctor: " << "ok" << "\n"; } ~TestStatic() { std::cout << "dctor: " << static_name() << "\n"; } } test; int main() { std::cout << static_name() << "\n"; } ``` Сначала отрабатывает конструктор `TestStatic`. Затем `main`, вызвав `static_name`, конструирует строку. По завершении программы СНАЧАЛА уничтожается строка, а затем деструктор `TestStatic` обращается к [уже уничтоженной строке](https://godbolt.org/z/b5Krcz). Чтобы избежать подобного, можно либо в конструкторе `TestStatic` вызвать функцию `static_name` — тогда конструктор строки завершится до завершения конструктора `TestStatic` и порядок уничтожения объктов будет другим. Либо (и так иногда делают) в принципе предотвратить уничтожение статической строки: [создать ее в куче](https://godbolt.org/z/j7aY7q). ```C++ const std::string& static_name() { static const std::string* name = new std::string("Hello! Hello! long long string!"); return *name; } ``` Но тогда вы соглашаетесь на утечку памяти. Конечно, никакой утечки на самом деле не будет — статический объект умрет при завершении работы программы. И память все равно будет освобождена. Однако утилиты, используемые для обнаружения утечек, обязательно укажут на ваш статический объект в куче. И вам придется их отфильтровывать, чтобы не мешали искать настоящие утечки.