Статический порядок инициализации фиаско
В своей книге "Мышление на С ++" (глава 10) Эккель описывает технику, впервые предложенную Джерри Шварцем для решения проблемы фиаско. Он говорит, что если мы хотим инициализировать x в 100 и y в 200 и разделить их между всеми единицами перевода, мы создадим Initializer.h, который выглядит следующим образом:
extern int x;
extern int y;
class Initializer {
static int initCount;
// if (initCount++ == 0) x = 100 & y = 200
/* ... */
};
static Initializer init;
И в файле реализации мы имеем
#include "Initializer.h"
int x;
int y;
int Initializer::initCount;
и Экель говорит, что "статическая инициализация (в файле реализации) приведет к обнулению всех этих значений".
Позвольте мне рассмотреть следующий случай: компилятор обрабатывает файл реализации после некоторого другого файла с включенным этим заголовком (это означает, что x и y уже были установлены на 100 и 200 в этом другом файле). Компилятор видит int x
так что же он будет делать? Устанавливает ли он x и y в ноль, исключая инициализацию и все возможные изменения в предыдущих файлах? Но если это так, то initCount
также будет установлен на ноль, ломая всю технику.
5 ответов
Но если это так, и компилятор обрабатывает файл реализации после какого-то другого файла, чем он установит x и y в ноль, исключая инициализацию и все возможные изменения в предыдущих файлах?
Я не уверен, что вы подразумеваете под этим. Если x
а также y
определены в других файлах, тогда у вас есть столкновение компоновщика, и программа просто не будет компилироваться.
Если x
, y
и, самое главное Initializer::initCount
реализованы таким образом, в программе будут их уникальные экземпляры; они эффективно глобальны и будут инициализированы 0
при запуске программы, перед любым Initializer
(из-за включения заголовка, объявляющего static
экземпляр этого класса). Каждая конструкция static Initializer
сначала проверим, есть ли другие Initializer
были построены из-за if (initCount++ == 0)
и т.п.
Первый Initializer
ctor для запуска (еще до входа main
), таким образом, установит все три значения.
В "Инициализаторе" выполняется присваивание, а не инициализация (при условии допустимого синтаксиса).
Как таковой, он "решает" фиаско статического порядка инициализации для вашего особого случая, потому что в первую очередь фиаско не происходит. x и y являются целыми числами, они не вызывают друг друга в непредсказуемое время, и, кроме того, они также живут в одной и той же единице перевода. Компилятор просто инициализирует их правильно. Это хорошо, если вы назначаете значения в определенном порядке впоследствии, но это только сложнее, а не лучше.
Чтобы фиаско с порядком статической инициализации появилось, вам понадобится такая ситуация: для конструктора x требуется значение y (или наоборот), и они находятся в разных единицах перевода. Таким образом, это шанс 50:50, работает ли это или нет.
Теперь структура "Initializer" будет правильно присваивать значения в определенном порядке, но в то время конструкторы x и y уже выполнялись, потому что вы не можете назначить то, что не было построено... так что это не будет избежать проблемы вообще, если она существовала.
Конструкция при первом использовании является распространенным способом решения этой проблемы. Существуют различные разновидности этого метода (каждый со своими преимуществами и недостатками), например:
x& get_x() { static x *xxx = new x(); return *xxx; }
Предполагая, что вы имеете в виду любое возможное использование и инициализацию в области статического инициализации в других исходных файлах, вы абсолютно правы: если компилятор решил выполнить статическую инициализацию этого файла после этого в других файлах, то вы отмените эту другую работу.
Во многих случаях вы можете избавить себя от огромного количества головной боли, просто не используя глобальные переменные / статические значения вообще.
Глобальные x и y будут инициализированы нулем при загрузке программы перед выполнением любого кода. Когда любой Инициализатор создан, x и y уже инициализированы в ноль. Вещи происходят в таком порядке:
- Программа загружена
- Глобальные и статические переменные инициализируются нулями (x и y получают 0 значений)
- Создаются глобальные объекты (инициализатор устанавливает значения x и y равными 100 и 200)
Почему бы не объявить (в области файла, в одной единице перевода):
int x = 100;
int y = 200;
x и y будут сохранены в разделе чтения / записи изображения, поэтому они инициализируются перед выполнением любого кода в процессе. Вам не нужно беспокоиться о порядке инициализации простых старых данных.