Статическое фиаско порядка инициализации
В своем "Думая на С++" (глава 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
также будет установлено в ноль, разрушая всю технику.
Ответы
Ответ 1
Но если это правда, и компилятор обрабатывает файл реализации после некоторого другого файла, чем он установит x и y в нуль, исключая инициализацию и все возможные изменения в предыдущих файлах?
Я не уверен, что вы подразумеваете под этим. Если x
и y
определены в других файлах, то у вас есть столкновение с компоновщиком, и программа просто не будет компилироваться.
Если x
, y
и, самое главное, Initializer::initCount
реализованы таким образом, в программе будут уникальные экземпляры; они эффективно глобальны и будут инициализированы до 0
при запуске программы, прежде чем будет сконструирован любой Initializer
(из-за включения заголовка, объявляющего экземпляр static
этого класса). Каждая конструкция a static Initializer
сначала проверит, были ли построены любые другие Initializer
из-за if (initCount++ == 0)
и т.д.
При первом запуске Initializer
ctor (до ввода main
) будут установлены все три значения.
Ответ 2
То, что делается в "Инициализаторе", - это назначение, а не инициализация (предполагая действительный синтаксис).
Таким образом, он "решает" фиаско порядка статического инициализации для вашего особого случая, потому что в первую очередь нет фиаско. x и y - целые числа, они не называют друг друга в непредсказуемое время, а вдобавок к этому они тоже живут в одной и той же части перевода. Компилятор просто инициализирует их правильно. Это нормально, если впоследствии вы назначаете значения в определенном порядке, но это только более сложный, не лучший.
Для появления фиаско порядка статической инициализации вам понадобится такая ситуация, как: конструктор x нуждается в значении y (или наоборот) и они находятся в разных единицах перевода. Таким образом, это шанс 50:50, работает ли это или нет.
Теперь структура "Инициализатор" будет правильно назначать значения в определенном порядке, но в это время конструкторы x и y уже выполняются, потому что вы не можете назначить то, что не было создано... так он не избежал бы проблемы вообще, если бы она существовала.
Конструкция при первом использовании - это общий способ решения этой проблемы. Существуют разные вкусы (каждый со своими преимуществами и недостатками) этого метода, например, например:
x& get_x() { static x *xxx = new x(); return *xxx; }
Ответ 3
Предполагая, что вы имеете в виду любое возможное использование и инициализацию в области static-init в других исходных файлах, тогда вы абсолютно правы: если компилятор решил запустить статический инициализации этого файла после этого в других файлах, вы отмените эту работу.
Во многих случаях вы можете сэкономить огромное количество головных болей, просто не используя глобалы/статику вообще.
Ответ 4
Глобальные x и y будут инициализированы до нуля, когда программа загружена, прежде чем какой-либо код будет выполнен. Когда создается любой инициализатор, x и y уже инициализируются до нуля. Все происходит в таком порядке:
- Загрузка программы
- Глобальные и статические переменные инициализируются нулем (x и y получают их значения 0)
- Создаются глобальные объекты (Инициализатор устанавливает x и y на 100 и 200)
Ответ 5
Почему бы не объявить (в области файла, в одной единице перевода):
int x = 100;
int y = 200;
x и y будут сохранены в секции чтения/записи изображения, чтобы они были инициализированы перед выполнением любого кода процесса. Вам не нужно беспокоиться о порядке инициализации для обычных старых данных.