web-dev-qa-db-fra.com

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 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 son constructeur et son destructeur sont-ils appelés?

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

La durée de vie de la fonction static variables commence la première fois[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 stipule que les destructeurs d’objets statiques doivent être exécutés dans l’ordre inverse de celui de leur construction.[1], et l'ordre de construction peut dépendre du programme spécifique exécuté, 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éé dans foo
Détruit dans foo

C:> sample.exe 1
Créé en si
Créé dans foo
Détruit dans foo
Détruit en si

C:> sample.exe 1 2
Créé dans foo
Créé en si
Détruit en si
Détruit dans foo

[0] Depuis C++ 98 [2] n'a pas de référence à plusieurs threads, la manière dont cela se comportera dans un environnement multi-threadé n'est pas spécifiée, et peut poser problème comme Roddy mentionne.

[1] C++ 98 section 3.6.3.1 [basic.start.term]

[2] Dans C++ 11, la statique est initialisée de manière sécurisée pour le thread, elle est également appelée magique statique .

235
Motti

Motti a raison sur la commande, mais il y a d'autres points à considérer:

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, les performances sont faibles, mais le problème 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ée à partir de plusieurs threads, vous pouvez être confronté à des conditions de concurrence entraînant une initialisation incorrecte de plonk, voire plusieurs fois. De plus, dans ce cas, plonk peut être détruit par un autre thread que celui qui l'a construit.

Malgré ce que dit la norme, je 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 à cerner.

123
Roddy

Les explications existantes ne sont pas vraiment 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 en lançant une exception, l'initialisation n'est pas terminée et sera réessayée lors du prochain contrôle par la déclaration. Si le contrôle entre la déclaration simultanément pendant l'initialisation de la variable, l'exécution simultanée doit attendre la fin de l'initialisation. Si le contrôle entre à nouveau la déclaration de manière récursive pendant l'initialisation de la variable, le comportement n'est pas défini.

10
Ben Voigt

FWIW, Codegear C++ Builder ne se détruit pas dans l’ordre attendu conformément au standard.

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
Roddy

Les variables statiques entrent en jeu dès que l'exécution du programme commence et rester disponible 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.

0
Chandra Shekhar