Jaki jest czas życia zmiennej statycznej w funkcji C++?

Jeśli zmienna jest zadeklarowana jako static w zakresie funkcji, jest inicjalizowana tylko raz i zachowuje swoją wartość między wywołaniami funkcji. Jaki dokładnie jest jego okres życia? Kiedy zostanie wywołany jego konstruktor i destruktor?

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

P. S. dla tych, którzy chcą wiedzieć Dlaczego zadałem pytanie, skoro już znałem odpowiedź?

Author: allyourcode, 2008-10-29

4 answers

Żywotność funkcji static zmiennych rozpoczyna się po raz pierwszy[0] przepływ programu napotka deklarację i kończy się po zakończeniu programu. Oznacza to, że run-time musi wykonać pewne prowadzenie ksiąg, aby go zniszczyć tylko jeśli został faktycznie zbudowany.

Dodatkowo, ponieważ norma mówi, że destruktory obiektów statycznych muszą działać w odwrotnej kolejności zakończenia ich budowy[1] a kolejność budowy może zależeć przy uruchomieniu konkretnego programu należy wziąć pod uwagę kolejność budowy.

Przykład

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);
}

Wyjście:

C: > próbka.exe
Created in foo
Destroyed in foo

C: > próbka.exe 1
Utworzone w if
Created in foo
Destroyed in foo
Destroyed in if

C: > próbka.exe 1 2
Created in foo
Utworzone w if
Zniszczone w if
Destroyed in foo

[0] od C++98[2] nie ma odniesienia do wielu wątków sposób zachowania się tego w środowisku wielowątkowym jest nieokreślony i może być problematyczny, jak wspomina Roddy.

[1] sekcja C++98 3.6.3.1 [podstawowe.zaczynaj.term]

[2] W C++11 statyki są inicjowane w bezpieczny sposób wątku, jest to również znane jako Magic statyka.

 197
Author: Motti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:26

Motti ma rację co do porządku, ale jest kilka innych rzeczy do rozważenia:

Kompilatory zazwyczaj używają ukrytej zmiennej flag, aby wskazać, czy statyka lokalna została już zainicjowana, a ta flaga jest sprawdzana przy każdym wpisie funkcji. Oczywiście jest to mały hit wydajności, ale co więcej niepokojące jest to, że ta flaga nie jest gwarantowana, aby być bezpieczny wątek.

Jeśli masz lokalną statykę jak wyżej, a' foo ' jest wywoływane z wielu wątków, możesz mieć race warunki powodujące niepoprawne lub nawet wielokrotne inicjowanie 'plonk'. Ponadto w tym przypadku "plonk" może zostać zniszczony przez inny wątek niż ten, który go skonstruował.

Pomimo tego, co mówi norma, byłbym bardzo ostrożny z rzeczywistą kolejnością lokalnej destrukcji statycznej, ponieważ jest możliwe, że możesz nieświadomie polegać na statycznej istocie nadal aktualnej po jej zniszczeniu, a to jest naprawdę trudne do wyśledzenia.

 119
Author: Roddy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-29 12:24:55

Istniejące wyjaśnienia nie są tak naprawdę kompletne bez rzeczywistej reguły ze standardu, znalezionego w 6.7:

Inicjalizacja zerowa wszystkich zmiennych z zakresu bloków o statycznym czasie przechowywania lub czasie przechowywania wątku jest wykonywana przed jakąkolwiek inną inicjalizacją. Stała inicjalizacja elementu zakresu bloku ze statycznym czasem przechowywania, jeśli ma to zastosowanie, jest wykonywana przed pierwszym wprowadzeniem bloku. Realizacja jest dozwolona do wykonania we wczesnym inicjalizacja innych zmiennych o statycznym lub wątkowym czasie przechowywania na tych samych warunkach, na jakich implementacja może statycznie zainicjować zmienną o statycznym lub wątkowym czasie przechowywania w przestrzeni nazw. W przeciwnym razie taka zmienna jest inicjalizowana przy pierwszym przejściu kontroli przez jej deklarację; taka zmienna jest uważana za zainicjalizowaną po zakończeniu jej inicjalizacji. Jeśli inicjalizacja zakończy się rzucając wyjątek, inicjalizacja nie jest kompletna, więc zostanie ponownie wypróbowana przy następnym wprowadzeniu deklaracji przez control. Jeżeli control wprowadzi deklarację jednocześnie, podczas gdy zmienna jest inicjalizowana, równoczesne wykonanie będzie czekać na zakończenie inicjalizacji. Jeśli control ponownie wprowadzi deklarację rekurencyjnie podczas inicjalizacji zmiennej, zachowanie jest niezdefiniowane.

 9
Author: Ben Voigt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-11 21:08:25

FWIW, Codegear C++Builder nie niszczy w oczekiwanej kolejności zgodnie ze standardem.

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

... to kolejny powód, by nie polegać na rozkazie zniszczenia!

 8
Author: Roddy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-29 16:27:17