Quelle est la durée de vie d'une variable statique dans une fonction C ++?
Question
Si une variable est déclarée comme statique
dans l'étendue d'une fonction, elle n'est initialisée qu'une fois et conserve sa valeur entre les appels de fonction. Quelle est exactement sa durée de vie? Quand appelle-t-on son constructeur et son destructeur?
void foo()
{
static string plonk = "When will I die?";
}
La solution
La durée de vie des variables de fonction static
commence la première fois que [0] le flux de programme rencontre la déclaration et se termine à la fin du programme. Cela signifie que le temps d'exécution doit effectuer une certaine comptabilité afin de le détruire uniquement s'il a été construit.
De plus, étant donné que la norme indique que les destructeurs d'objets statiques doivent s'exécuter dans l'ordre inverse de l'achèvement de leur construction [1] , l'ordre de construction peut dépendre de l'exécution du programme 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);
}
Résultat:
C: > sample.exe
Créé dans foo
Détruit dans fooC: > sample.exe 1
Créé en si
Créé dans foo
Détruit dans foo
Détruit dans siC: > sample.exe 1 2
Créé dans foo
Créé en si
Détruit dans si
Détruit dans foo
[0]
Etant donné que C ++ 98 [2] ne contient aucune référence à plusieurs threads, son comportement dans une multi- L’environnement threadé n’est pas spécifié et peut poser problème car Roddy mentionne.
[1]
C ++ 98 section 3.6.3.1
[basic.start.term]
[2]
En C ++ 11, les statiques sont initialisées de manière sécurisée pour les threads. Cette action est également appelée Statique magique .
Autres conseils
Motti a raison en ce qui concerne l'ordre, mais il y a d'autres éléments à prendre en compte:
Les compilateurs utilisent généralement une variable d'indicateur caché pour indiquer si la statique locale a déjà été initialisée et cet indicateur est vérifié à chaque entrée de la fonction. Évidemment, il s’agit là d’une petite perte de performances, mais le problème, c’est que ce drapeau n’est pas garanti comme étant thread-safe.
Si vous avez une statique locale comme ci-dessus et que foo
est appelé à partir de plusieurs threads, vous pouvez avoir des conditions de concurrence entraînant l'initialisation incorrecte de plonk
, voire plusieurs fois. De plus, dans ce cas, plonk
peut être détruit par un thread différent de celui qui l'a construit.
Malgré ce que dit la norme, je me méfierais beaucoup de l'ordre de destruction statique local, car il est possible que vous dépendiez involontairement d'un caractère statique toujours valide après sa destruction, ce qui est très difficile à suivre. vers le bas.
Les explications existantes ne sont pas complètes sans la règle actuelle de la norme, trouvée dans 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é de portée de bloc avec une durée de stockage statique, le cas échéant, est effectuée avant la première entrée de son bloc. Une implémentation est autorisée à effectuer une initialisation précoce d'autres variables de portée de bloc avec une durée de stockage statique ou d'unités d'exécution dans les mêmes conditions qu'une implémentation est autorisée à initialiser de manière statique une variable avec une durée de stockage statique ou d'une durée d'exécution dans l'étendue de l'espace de noms. 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 comme initialisée à la fin de son initialisation. Si l'initialisation se termine par une exception, l'initialisation n’est pas terminé, il sera réessayé la prochaine fois que le contrôle entrera dans la déclaration. Si le contrôle entre la déclaration simultanément alors que la variable est en cours d'initialisation, l'exécution simultanée doit attendre la fin de l'initialisation. Si le contrôle ré-entre la déclaration de manière récursive pendant l’initialisation de la variable, le comportement est indéfini.
FWIW, Codegear C ++ Builder ne se détruit pas dans l'ordre attendu conformément à 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!
Les variables statiques entrent en jeu une fois que l'exécution du programme commence et restent disponibles jusqu'à la fin de l'exécution du programme.
Les variables statiques sont créées dans le segment de données de la mémoire .