Статическое фиаско порядка инициализации

В своем "Думая на С++" (глава 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 будут сохранены в секции чтения/записи изображения, чтобы они были инициализированы перед выполнением любого кода процесса. Вам не нужно беспокоиться о порядке инициализации для обычных старых данных.