2016-05-06 2 views
0

Disons que je crée:Risque de partage de variable statique locale d'une méthode entre instances?

class Hello { 
    public: 
     int World(int in) 
     { 
      static int var = 0; // <<<< This thing here. 
      if(in >= 0) { 
       var = in; 
      } else { 
       cout << var << endl; 
      } 
     } 
}; 

Maintenant, si je fais:

Hello A; 
Hello B; 

A.World(10); 
A.World(-1); 
B.World(-1); 

Je reçois la sortie de "10" suivie d'une autre "10". La valeur de la variable locale d'une méthode vient de passer d'une instance d'une classe à une autre.

Ce n'est pas surprenant - techniquement, les méthodes ne sont que des fonctions avec un paramètre caché this, donc une variable locale statique devrait se comporter comme dans les fonctions communes. Mais est-ce garanti? Est-ce un comportement imposé par la norme, ou est-ce simplement un sous-produit heureux de la façon dont le compilateur gère les méthodes? En d'autres termes, ce comportement est-il sûr à utiliser? (... au-delà du risque standard de dérouter quelqu'un d'inhabituel ...)

Répondre

3

Oui. Peu importe si la fonction est un membre [non-static] d'une classe ou non, il est garanti qu'elle n'a qu'une seule instance de ses variables statiques.

Une bonne explication technique pour de telles variables est que ce sont des objets avec static duration et internal linkage - et donc ces noms vivent jusqu'à ce que le programme se termine, et toutes les instances de ce nom se réfèrent à la même entité.

+0

Tout aussi important que la durée de stockage est le fait que le nom 'var' a maintenant * lien interne * (par opposition à" pas de liaison "), et donc toutes les instances du nom se réfèrent à la même entité. –

+0

@KerrekSB, chose certaine. Ajouterai. – SergeyA

0

Si nous parlons de Windows du compilateur, il est garanti

https://msdn.microsoft.com/en-us/library/y5f6w579.aspx

L'exemple suivant montre une variable locale déclarée statique dans une fonction membre. La variable statique est disponible pour l'ensemble du programme; toutes les instances du type partagent la même copie de la variable statique.

Ils utilisent un exemple très similaire au vôtre. Je ne connais pas GCC

0

Maintenant, pour répondre à la question "Tout risque de partage de variable statique locale d'une méthode entre instances?" ça pourrait être un peu moins simple. Il peut y avoir des risques potentiels dans l'initialisation et l'utilisation de la variable et ces risques sont spécifiques aux variables locales à la méthode (par opposition aux variables de classe).

Pour l'initialisation, une partie pertinente de la norme est de 6,7/4 [stmt.dcl]:

initialisation dynamique d'une variable de champ de bloc de mémoire statique durée (3.7.1) ou de fils durée de stockage (3.7.2) est effectuée le le premier contrôle de temps 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, il sera donc réessayé la prochaine fois que le contrôle entre la déclaration. Si le contrôle entre simultanément la déclaration pendant l'initialisation de la variable, l'exécution simultanée doit attendre la fin de l'initialisation.Si le contrôle rentre récursivement dans la déclaration alors que la variable est , le comportement est indéfini.

Dans les cas simples, tout devrait fonctionner comme prévu. Lorsque la construction et l'initialisation de la variable est plus complexe, il y aura des risques spécifiques à ce cas. Par exemple, si le constructeur jette, il aura l'occasion de lancer à nouveau lors de l'appel suivant. Un autre exemple serait l'initialisation récursive qui est un comportement indéfini.

Un autre risque possible est la performance de la méthode. Le compilateur devra implémenter un mécanisme pour assurer l'initialisation conforme de la variable. Cela dépend de l'implémentation et il pourrait très bien s'agir d'un verrou pour vérifier si la variable est initialisée, et ce verrou pourrait être exécuté à chaque fois que la méthode est appelée. Lorsque cela se produit, cela peut avoir un effet négatif significatif sur les performances.

+0

Aucun compilateur qui verrouille vraiment le garde à chaque entrée dans la fonction ne mérite d'être appelé un compilateur en 2016. – SergeyA

+0

@SergeyA C'est un très bon point et toute personne qui a le choix devrait utiliser un bon compilateur. Pour ceux qui doivent travailler dans des environnements plus anciens, il peut être utile de vérifier l'impact sur la performance lorsqu'il s'agit d'un facteur critique. Par exemple, avec gcc 4.4.7, les variables locales statiques peuvent introduire une surcharge de plusieurs cycles de CPU à chaque appel de fonction. –

+0

Bien, vous ne devriez pas substituer des sujets, devriez-vous? Il y a un certain overhead (move + branch) associé à des variables statiques (était là depuis le début des temps, personne ne le nie). Et la plupart des gens peuvent vivre avec ça. Cependant, vous avez affirmé que 'lock peut être exécuté à chaque fois que la méthode est appelée' - et c'est une chose très différente, avec laquelle la plupart des gens ne peuvent pas vivre. – SergeyA

1

Juste une chose à ajouter à la bonne réponse. Si votre classe était basée sur un modèle, l'instance de var ne serait partagée que parmi les objets du même type d'instanciation. Donc, si vous aviez:

template<typename C> 
class Hello { 
     public: 
     int World(int in) 
     { 
       static int var = 0; // <<<< This thing here. 
       if(in >= 0) { 
         var = in; 
       } else { 
         cout << var << endl; 
       } 
     } 
}; 

Et puis:

Hello<int> A; 
Hello<int> B; 
Hello<unsigned> C; 

A.World(10); 
A.World(-1); 
B.World(-1); 
C.World(-1); 

Ensuite, la sortie finale serait « 0 » plutôt que « 10 », parce que le Hello<unsigned> instanciation aurait sa propre copie de var.

+0

Une mise en garde importante! –