Когда инициализируются статические члены класса C ++? - PullRequest
60 голосов
/ 14 сентября 2009

Кажется, нет простого ответа на этот вопрос, но есть ли какие-либо предположения, которые можно безопасно сделать, когда можно получить доступ к полю статического класса?

РЕДАКТИРОВАТЬ: кажется, что единственное безопасное предположение состоит в том, что все статические данные инициализируются до начала программы (вызов main). Итак, пока я не ссылаюсь на статику из другого кода статической инициализации, мне не о чем беспокоиться?

Ответы [ 7 ]

53 голосов
/ 14 сентября 2009

Стандарт гарантирует две вещи - что объекты, определенные в одной и той же единице перевода (обычно это означает, что файл .cpp) инициализируются в порядке их определений ( не декларации ):

3.6.2

Хранилище для объектов со статической продолжительностью хранения (basic.stc.static) должно быть инициализировано нулем (dcl.init) перед любой другой инициализацией. Обнуление инициализации и инициализация с постоянным выражением вместе называются статической инициализацией; все остальные инициализации - это динамическая инициализация. Объекты типов POD (basic.types) со статической продолжительностью хранения, инициализированные с помощью константных выражений (expr.const), должны быть инициализированы перед любой динамической инициализацией. Объекты со статической продолжительностью хранения, определенной в области пространства имен в одной и той же единице перевода и динамически инициализированной, должны быть инициализированы в том порядке, в котором их определение появляется в единице перевода.

Другая гарантированная вещь заключается в том, что инициализация статических объектов из модуля перевода будет выполнена до использования какого-либо объекта или функции из этого модуля перевода:

Определяется реализацией независимо от того, выполняется ли динамическая инициализация (dcl.init, class.static, class.ctor, class.expl.init) объекта области имен перед первым оператором main. Если инициализация откладывается до некоторого момента времени после первого утверждения main, она должна произойти до первого использования любой функции или объекта, определенных в той же единице перевода, что и объект, который должен быть инициализирован.

Ничего другого я не гарантировал (особенно порядок инициализации объектов, определенных в разных единицах перевода, определяется реализацией).

EDIT Как указано в комментарии Сума, также гарантируется, что они инициализируются до ввода main.

17 голосов
/ 14 сентября 2009

Они инициализируются до запуска программы (т. Е. До ввода main).

Если в одном файле CPP имеется два или более определений (статических данных), они инициализируются в той последовательности, в которой они определены в файле (инициализируется ранее определенное / более высокое значение в файле). до следующего).

Когда имеется два или более определений (статических данных) в нескольких файлах CPP, последовательность обработки файлов CPP не определена / зависит от реализации. Это проблема, если конструктор глобальной переменной (вызываемой перед запуском программы) ссылается на другую глобальную переменную, определенную в другом файле CPP, которая, возможно, еще не была создана. Тем не менее, пункт 47 Effective C ++ Мейерса (который называется Убедитесь, что глобальные объекты инициализируются перед использованием ) действительно описывает обходной путь ...

  • Определить статическую переменную в заголовочном файле (она является статической, поэтому вы можете иметь несколько ее экземпляров без жалоб компоновщика)

  • Пусть конструктор этой переменной вызывает все, что вам нужно (в частности, создает глобальные синглеты, объявленные в заголовках)

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

3 голосов
/ 22 июля 2015

Ваш окончательный вывод в Правке верен. Но проблема в том, что класс сам по себе статичен. Проще сказать, что в моем коде будут статические члены класса, которые не ссылаются на другие глобальные статические члены данных / класса, но как только вы выберете этот путь, скоро все пойдет не так. Один из подходов, который я нашел полезным на практике, состоит не в том, чтобы иметь классовые члены-статические данные, а классические методы-обертки. Эти методы могут затем держать статический объект внутри себя. Например, </p> <pre><code>TypeX* Class2::getClass1Instance() { static TypeX obj1; return &obj1; }

Примечание: более ранний ответ гласит:

Другая гарантированная вещь состоит в том, что инициализация статических объектов из блока перевода будет сделано перед использованием любого объекта или функция из этого блока перевода

Это не совсем правильно, и стандарт здесь выводится неверно. Это может не соответствовать действительности, если функция из модуля перевода вызывается до ввода main.

1 голос
/ 14 сентября 2009

Их можно инициализировать в файлах реализации (.c / cpp / cc). Не инициализируйте их в .h, так как компилятор будет жаловаться на множественные определения.

Обычно они инициализируются до main, однако порядок неизвестен, поэтому избегайте зависимостей. К ним, безусловно, можно получить доступ в функции-члене. Имейте в виду, порядок инициализации неизвестен для статических членов. Я бы предложил инкапсулировать статический член в статическую функцию, которая проверит, был ли этот элемент инициализирован.

1 голос
/ 14 сентября 2009

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

0 голосов
/ 14 сентября 2009

я думаю, что основной поток процесса выполнит следующие пять шагов в порядке

  1. инициализация библиотеки CRT

  2. статическая инициализация

  3. выполнение функции main ()

  4. статическая унитализация

  5. униализация библиотеки CRT

вы хотите ссылочную статику из другого кода статической инициализации? возможно, работают следующие коды:

class A;
static auto_ptr<A> a(auto_ptr<A>(&GetStaticA()));
A &GetStaticA(void)
{
    static A *a = NULL; //the static basic type variables initialized with constant experession will be initialized earlier than the other static ones
    if (a == NULL)
    {
         a = new A();
         return *a;
    }
}
0 голосов
/ 14 сентября 2009

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

РЕДАКТИРОВАТЬ: Чтобы уточнить, ваше добавленное предположение правильно. Пока вы получаете доступ к нему только после основной записи, вам не нужно беспокоиться о том, когда и как он будет инициализирован. К этому времени он будет инициализирован.

...