Quelle est la durée de vie d'une variable statique dans une fonction C++?

si une variable est déclarée comme static dans la portée d'une fonction, elle n'est initialisée qu'une seule fois et conserve sa valeur entre les appels de fonction. Quelle est exactement sa durée de vie? Quand faire son constructeur et le destructeur appelé?

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

pour ceux qui veulent savoir pourquoi j'ai posé la question si je connaissais déjà la réponse?

313
demandé sur allyourcode 2008-10-29 15:14:03

4 réponses

La durée de vie de la fonction static variables débute la première fois [0] le flux de programme des rencontres de la déclaration, et il se termine à la fin du programme. Cela signifie que l'exécution doit effectuer un certain livre en gardant à l'ordre de détruire que si elle a été effectivement construit.

en outre, puisque la norme stipule que les destructeurs d'objets statiques doivent fonctionner dans l'ordre inverse de l'achèvement de leur construction [1] et l'ordre de construction peut dépendre du programme d'exécution spécifique, l'ordre de construction doit être pris en compte.

exemple

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

sortie:

C: > échantillon.exe

Créé en foo

Détruit en fo 151990920"

C: > échantillon.exe 1

Créé en si

Créé en foo

Détruit en foo

Détruit en si

C: > échantillon.exe 1 2
1519250920" Créé en foo

Créé en si

Détruit en si

Détruit en fo 151990920"

[0] Depuis C++98 [2] n'a aucune référence à plusieurs threads comment cela va se comporter dans un environnement multi-threaded est indéterminée, et peut être problématique comme Roddy mentions.

[1] C++98 de la section 3.6.3.1 [de base.commencer.terme]

[2] en C++11 statics sont initialisés d'une manière sûre, ce qui est également connu sous le nom de Magie Statique .

197
répondu Motti 2017-05-23 12:10:26

Motti a raison au sujet de l'ordre, mais il y a d'autres choses à considérer:

Les compilateurs

utilisent généralement une variable de drapeau cachée pour indiquer si la statique locale a déjà été initialisée, et ce drapeau est vérifié sur chaque entrée de la fonction. De toute évidence, il s'agit d'un petit succès de performance, mais ce qui est plus préoccupant est que ce drapeau n'est pas garanti d'être sans fil.

si vous avez une statique locale comme ci-dessus, et 'foo' est appelé à partir de plusieurs threads, vous pouvez avoir des conditions de course provoquant 'plonk' à être initialisé incorrectement ou même plusieurs fois. En outre, dans ce cas,' plonk ' peut être détruit par un fil différent de celui qui l'a construit.

malgré ce que dit la norme, je serais très méfiant de l'ordre réel de destruction statique locale, parce qu'il est possible que vous puissiez involontairement compter sur un être statique encore valide après qu'il a été détruit, et c'est vraiment difficile à suivre vers le bas.

119
répondu Roddy 2008-10-29 12:24:55

Les explications ne sont pas vraiment complet sans la règle de la Norme, trouvé en 6.7:

l'initialisation à zéro de toutes les variables de portée de bloc avec une durée de stockage statique ou une durée de stockage de thread est effectuée avant toute autre initialisation. L'initialisation constante d'une entité à portée de bloc avec une durée de stockage statique, le cas échéant, est effectuée avant que son bloc ne soit entré pour la première fois. Une mise en œuvre est autorisé à effectuer l'initialisation précoce d'autres variables de champ d'application de bloc avec la durée de stockage statique ou thread dans les mêmes conditions qu'une implémentation est autorisé à initialiser statiquement une variable avec la durée de stockage statique ou thread dans le champ d'application namespace. Sinon, une telle variable est initialisée la première fois que le contrôle passe par sa déclaration; une telle variable est considérée initialisée à la fin de son initialisation. Si l'initialisation de la sortie lancer une exception, l'initialisation n'est pas complète, il sera donc essayé à nouveau la prochaine fois que le contrôle entre dans la déclaration. Si control entre la déclaration concurremment pendant que la variable est initialisée, l'exécution concurrente doit attendre l'achèvement de l'initialisation. Si control ré-entre la déclaration de façon récursive pendant que la variable est initialisée, le comportement n'est pas défini.

9
répondu Ben Voigt 2014-09-11 21:08:25

FWIW, Codegear C++Builder n'est pas de détruire dans l'ordre attendu selon la norme.

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

... ce qui est une autre raison de ne pas compter sur l'ordre de destruction!

8
répondu Roddy 2008-10-29 16:27:17