Статический порядок инициализации фиаско - PullRequest
10 голосов
/ 14 марта 2011

В своей книге «Мышление на С ++» (глава 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 ]

4 голосов
/ 14 марта 2011

Но если это так, и компилятор обрабатывает файл реализации после какого-то другого файла, то он установит x и y в ноль, исключая инициализацию и все возможные изменения в предыдущих файлах?

Я не уверен, что вы подразумеваете под этим.Если x и y определены в других файлах, то у вас есть конфликт компоновщика, и программа просто не будет компилироваться.

Если x, y и, самое главное, Initializer::initCountреализованный таким образом, в программе будут уникальные экземпляры;они фактически глобальны и будут инициализированы в 0 при запуске программы, за до создания любого Initializer (из-за включения заголовка, объявляющего экземпляр static этого класса).Каждая конструкция static Initializer сначала проверяет, были ли построены какие-либо другие Initializer из-за if (initCount++ == 0) и т. Д.

Первый запуск Initializer ctor (еще до ввода main)при этом будут установлены все три значения.

3 голосов
/ 14 марта 2011

В «Инициализаторе» выполняется присвоение, а не инициализация (при условии допустимого синтаксиса).

Как таковой, он «решает» фиаско статического порядка инициализации для вашего особого случая, , потому что фиаско в первую очередь отсутствует . x и y являются целыми числами, они не вызывают друг друга в непредсказуемое время, и, кроме того, они также живут в одной и той же единице перевода. Компилятор просто правильно их инициализирует. Это нормально, если вы назначаете значения в определенном порядке впоследствии, но это только сложнее, а не лучше.

Чтобы фиаско статического порядка инициализации появилось, вам понадобится такая ситуация: для конструктора x требуется значение y (или наоборот), и они находятся в разных единицах перевода. Таким образом, с вероятностью 50:50 это работает или нет.

Теперь структура "Initializer" будет правильно присваивать значения в определенном порядке, но в это время конструкторы для x и y уже запущены, потому что вы можете ' присваивать то, что не было построено ... так что это вообще не позволило бы избежать проблемы, если бы она существовала.

Построение при первом использовании - распространенный способ решения этой проблемы. Существуют различные разновидности этого метода (каждый со своими преимуществами и недостатками), например, например:

x& get_x() { static x *xxx = new x(); return *xxx; }
2 голосов
/ 14 марта 2011

Предполагая, что вы имеете в виду любое возможное использование и инициализацию в области статического инициализации в других исходных файлах, вы абсолютно правы: если компилятор решил выполнить статическую инициализацию этого файла после этого в других файлах, то вы отмените эту другую работу. .

Во многих случаях вы можете избавить себя от огромного количества головных болей, просто не используя глобалы / статику вообще.

1 голос
/ 14 марта 2011

Глобальные x и y будут инициализированы в ноль при загрузке программы, прежде чем какой-либо код будет выполнен.Когда любой Инициализатор создан, x и y уже инициализированы в ноль.Все происходит в следующем порядке:

  1. Программа загружена
  2. Глобальные и статические переменные инициализируются нулями (x и y получают 0 значений)
  3. Создаются глобальные объекты(Инициализатор устанавливает x и y на 100 и 200)
0 голосов
/ 20 сентября 2012

Почему бы не объявить (в области файла, в одной единице перевода):

int x = 100;
int y = 200;

x и y будут сохранены в секции чтения / записи изображения, поэтому они инициализируются до выполнения любого кода в процессе. Вам не нужно беспокоиться о порядке инициализации простых старых данных.

...