Каково время жизни статической переменной в функции C++?
Если переменная объявлена как static
в области действия функции она инициализируется только один раз и сохраняет свое значение между вызовами функции. Что именно это время жизни? Когда вызывается его конструктор и деструктор?
void foo()
{
static string plonk = "When will I die?";
}
4 ответа
Время жизни функции 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
Уничтожено в Фу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.
Мотти прав насчет порядка, но есть некоторые другие вещи, которые следует учитывать:
Компиляторы обычно используют переменную скрытого флага, чтобы указать, была ли локальная статика уже инициализирована, и этот флаг проверяется при каждой записи в функцию. Очевидно, это небольшой удар по производительности, но больше всего беспокоит то, что этот флаг не гарантированно является потокобезопасным.
Если у вас есть локальная статика, как указано выше, и 'foo' вызывается из нескольких потоков, у вас могут возникнуть условия гонки, из-за которых 'plonk' инициализируется неправильно или даже несколько раз. Кроме того, в этом случае 'plonk' может быть разрушен другим потоком, чем тот, который его построил.
Несмотря на то, что говорится в стандарте, я бы очень настороженно отнесся к фактическому порядку локального статического разрушения, потому что вполне возможно, что вы можете невольно полагаться на статичность, действующую после того, как она была разрушена, и это действительно трудно отследить.
Существующие объяснения на самом деле не полны без действительного правила из Стандарта, найденного в 6.7:
Инициализация нуля всех переменных области блока со статической продолжительностью хранения или продолжительностью хранения потока выполняется перед любой другой инициализацией. Постоянная инициализация объекта области блока со статической продолжительностью хранения, если это применимо, выполняется перед первым вводом его блока. Реализация может выполнять раннюю инициализацию других переменных области блока со статическим или потоковым сроком хранения при тех же условиях, в которых реализация может статически инициализировать переменную со статическим или потоковым сроком хранения в области пространства имен. В противном случае такая переменная инициализируется при первом прохождении контроля через ее объявление; такая переменная считается инициализированной после завершения ее инициализации. Если инициализация завершается выдачей исключения, инициализация не завершена, поэтому она будет повторена при следующем входе элемента управления в объявление. Если элемент управления вводит объявление одновременно во время инициализации переменной, параллельное выполнение должно ожидать завершения инициализации. Если элемент управления повторно вводит объявление рекурсивно во время инициализации переменной, поведение не определено.
FWIW, Codegear C++Builder не разрушает в ожидаемом порядке в соответствии со стандартом.
C:\> sample.exe 1 2
Created in foo
Created in if
Destroyed in foo
Destroyed in if
... что является еще одной причиной не полагаться на порядок уничтожения!
В переменных Статическом пришел в игру, как только начинает выполнение программы и остается доступными до концов выполнения программы.
Статические переменные создаются в сегменте данных памяти.