Каково время жизни статической переменной в функции C++?


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

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

P. S. Для тех, кто хочет знать почему я задал вопрос, если я уже знал ответ?

4 313

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: > образец.exe
Создано в foo
Уничтожен в foo

C: > образец.exe 1
Создан в если
Создано в foo
Уничтожен в foo
Уничтожено в том случае, если

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

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

[1]C++98 раздел 3.6.3.1[основной.начать.термин]

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

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

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

Если у вас есть локальная статика, как указано выше, и " 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

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