Каково время жизни статической переменной в функции C ++? - PullRequest
347 голосов
/ 29 октября 2008

Если переменная объявлена ​​как static в области действия функции, она инициализируется только один раз и сохраняет свое значение между вызовами функции. Что именно это время жизни? Когда вызывается его конструктор и деструктор?

void foo() 
{ 
    static string plonk = "When will I die?";
}

Ответы [ 4 ]

229 голосов
/ 29 октября 2008

Время жизни переменных функции static начинается в первый раз [0] поток программы встречает объявление и заканчивается при завершении программы. Это означает, что среда выполнения должна вести какую-то бухгалтерию, чтобы разрушить ее только в том случае, если она действительно была построена.

Кроме того, поскольку в стандарте говорится, что деструкторы статических объектов должны работать в порядке, обратном завершению их построения [1] , и порядок построения может зависеть от конкретного запуска программы, порядок строительства должен быть принят во внимание.

Пример

struct emitter {
    string str;
    emitter(const string& s) : str(s) { cout << "Created " << str << endl; }
    ~emitter() { cout << "Destroyed " << str << endl; }
};

void foo(bool skip_first) 
{
    if (!skip_first)
        static emitter a("in if");
    static emitter b("in foo");
}

int main(int argc, char*[])
{
    foo(argc != 2);
    if (argc == 3)
        foo(false);
}

Выход:

C:> SAMPLE.EXE
Создано в foo
Уничтожено в foo

C:> sample.exe 1
Создано в
Создано в foo
Уничтожено в Фу
Уничтожено в случае

C:> sample.exe 1 2
Создано в foo
Создано в
Уничтожено, если
Уничтожено в Фу

[0] Поскольку C ++ 98 [2] не имеет ссылки на несколько потоков, то, как это будет вести себя в многопоточной среде, не определено и может быть проблематичным как упоминает Родди .

[1] C ++ 98 раздел 3.6.3.1 [basic.start.term]

[2] В C ++ 11 статика инициализируется потокобезопасным способом, это также известно как Magic Statics .

124 голосов
/ 29 октября 2008

Мотти прав насчет порядка, но есть еще несколько вещей, которые следует учитывать:

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

Если у вас локальная статика, как указано выше, и 'foo' вызывается из нескольких потоков, у вас могут быть условия гонки, из-за которых 'plonk' инициализируется неправильно или даже несколько раз. Кроме того, в этом случае 'plonk' может быть разрушен потоком, отличным от того, который его сконструировал.

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

10 голосов
/ 12 сентября 2014

Существующие объяснения на самом деле не полны без действительного правила из Стандарта, найденного в 6.7:

Инициализация нуля всех переменных области блока со статической продолжительностью хранения или продолжительностью хранения потока выполняется перед любой другой инициализацией. Постоянная инициализация объекта области блока со статической продолжительностью хранения, если применимо, выполняется перед первым вводом его блока. Реализации разрешается выполнять раннюю инициализацию других переменных области блока со статической или длительностью хранения потока при тех же условиях, что реализации разрешается статически инициализировать переменную со статической или продолжительностью хранения потока в области пространства имен. В противном случае такая переменная инициализируется при первом прохождении контроля через ее объявление; такая переменная считается инициализированной после завершения ее инициализации. Если инициализация завершается с помощью исключения, инициализация не завершен, поэтому он будет повторен при следующем входе элемента управления в объявление. Если во время инициализации переменной элемент управления вводит объявление одновременно, параллельное выполнение должно ожидать завершения инициализации. Если элемент управления повторно вводит объявление рекурсивно во время инициализации переменной, поведение не определено.

8 голосов
/ 29 октября 2008

FWIW, Codegear C ++ Builder не уничтожает в ожидаемом порядке в соответствии со стандартом.

C:\> sample.exe 1 2
Created in foo
Created in if
Destroyed in foo
Destroyed in if

... что является еще одной причиной не полагаться на порядок уничтожения!

...