Существует несколько подходов, чтобы убедиться, что глобальные переменные инициализируются перед их использованием.Тем не менее, они, как правило, зависят от платформы, а детали отличаются в зависимости от системы.Типичным примером глобальных объектов, которые должны быть инициализированы перед выполнением любого пользовательского кода, включая инициализацию глобальной переменной, являются объекты глобальных потоков, в частности std :: cout.Есть три подхода, с которыми я играл, чтобы убедиться, что std :: cout инициализируется рано:
- Стандартная библиотека уже определяет
std::ios_base::Init
, который подсчитывает, как часто она создавалась.Имея static std::ios_base::Init _Init;
в заголовке <iostream>
, объекты глобального потока могут быть инициализированы с использованием размещения new
, когда счетчик изменяется с 0 на 1, и явно уничтожаются, когда счетчик изменяется с 1 на 0 во время уничтожения.Чтобы избежать двойной инициализации, определение std::cout
просто зарезервирует необходимое пространство, например, char std::cout[sizeof(_Standard_output_stream)];
.Этот подход может сломаться, если пользовательский код определяет объекты до включения <iostream>
. - . При загрузке общих библиотек выполняется некоторый код инициализации до того, как объекты в общей библиотеке становятся доступными (аналогично некоторый код уничтожения при выгрузке общей библиотеки).).Помещая соответствующие объекты в общую библиотеку, код инициализации может быть запущен до того, как объект будет доступен.Основным недостатком является то, что это требует использования разделяемых библиотек, что не всегда желательно.
- Компоновщик эффективно формирует список инициализаций.В некоторых системах гарантируется, что порядок, в котором создаются различные объектные файлы, является обратным порядком видимых объектных файлов.То есть размещение, например,
std::cout
в последнем объектном файле в последней библиотеке [C ++] приводит к инициализации этого объекта перед другими объектами.Так как пользователи имеют контроль над линией ссылок, это не обязательно работает.
Самый безопасный подход в этом списке - поместить объекты в общую библиотеку.Наилучший подход, вероятно, состоит в том, чтобы избежать проблемы путем доступа к глобальному объекту через функцию.Все подходы требуют дополнительной работы, если потоки могут быть запущены до начала построения.Кроме того, я уверен, что есть подходы, специфичные для платформ, которые занимаются этим.Общее наблюдение состоит в том, что глобальные объекты являются злом: старайтесь не использовать их!