web-dev-qa-db-fra.com

Quand les membres statiques de la classe C ++ sont-ils initialisés?

Il ne semble pas y avoir de réponse facile à cela, mais y a-t-il des hypothèses qui peuvent être formulées en toute sécurité quant à l'accès à un champ de classe statique?

EDIT: La seule hypothèse sûre semble être que toutes les statistiques sont initialisées avant le début du programme (appel à main). Donc, tant que je ne fais pas référence à la statique d'un autre code d'initialisation statique, je ne devrais avoir aucun souci?

60
Tony the Pony

La norme garantit deux choses - que les objets définis dans la même unité de traduction (généralement cela signifie fichier .cpp) sont initialisés dans l'ordre de leurs définitions (pas de déclarations):

3.6.2

Le stockage des objets avec une durée de stockage statique (basic.stc.static) doit être initialisé à zéro (dcl.init) avant toute autre initialisation. L'initialisation zéro et l'initialisation avec une expression constante sont appelées collectivement initialisation statique; toute autre initialisation est une initialisation dynamique. Les objets de types POD (basic.types) avec une durée de stockage statique initialisée avec des expressions constantes (expr.const) doivent être initialisés avant toute initialisation dynamique. Les objets dont la durée de stockage statique est définie dans la portée de l'espace de noms dans la même unité de traduction et initialisés dynamiquement doivent être initialisés dans l'ordre dans lequel leur définition apparaît dans l'unité de traduction.

L'autre chose garantie est que l'initialisation des objets statiques à partir d'une unité de traduction sera effectuée avant l'utilisation de tout objet ou fonction de cette unité de traduction:

Il est défini par l'implémentation que l'initialisation dynamique (dcl.init, class.static, class.ctor, class.expl.init) d'un objet de portée d'espace de noms soit effectuée avant la première instruction de main. Si l'initialisation est différée à un certain moment dans le temps après la première déclaration de main, elle doit avoir lieu avant la première utilisation de toute fonction ou objet défini dans la même unité de traduction que l'objet à initialiser.

Rien d'autre n'est garanti (en particulier l'ordre d'initialisation des objets définis dans différentes unités de traduction est défini par l'implémentation).

EDIT Comme indiqué dans le commentaire de Suma, il est également garanti qu'ils sont initialisés avant la saisie de main.

54
Tadeusz Kopec

Ils sont initialisés avant le démarrage du programme (c'est-à-dire avant la saisie de main).

Lorsqu'il y a deux définitions ou plus (de données statiques) dans un seul fichier CPP, elles sont initialisées dans l'ordre dans lequel elles sont définies dans le fichier (celle définie précédemment/plus haut dans le fichier est initialisée avant la suivante l'un est).

Lorsqu'il y a deux définitions ou plus (de données statiques) dans plusieurs fichiers CPP, la séquence dans laquelle les fichiers CPP sont traités n'est pas définie/spécifique à l'implémentation. C'est un problème si le constructeur d'une variable globale (appelée avant le démarrage du programme) fait référence à une autre variable globale définie dans un fichier CPP différent, qui n'a peut-être pas encore été construit. Cependant, l'article 47 de Meyers 'Effective C++ (qui est intitulé Assurez-vous que les objets globaux sont initialisés avant d'être utilisés) décrit une solution de contournement ...

  • Définissez une variable statique dans un fichier d'en-tête (elle est statique afin que vous puissiez en avoir plusieurs instances sans que l'éditeur de liens ne se plaint)

  • Demandez au constructeur de cette variable d'invoquer tout ce dont vous avez besoin (en particulier, construisez les singletons globaux déclarés dans les en-têtes)

... qui, selon elle, est une technique qui peut être utilisée dans certains fichiers d'en-tête du système, par exemple pour vous assurer que la variable globale cin est initialisée avant même que les constructeurs de vos variables statiques l'utilisent.

18
ChrisW

Votre conclusion finale dans l'édition est correcte. Mais le problème est la classe statique elle-même. Il est plus facile de dire que mon code aura des membres statiques de classe qui ne font pas référence à d'autres données statiques globales/membres statiques de classe, mais une fois que vous emprunterez cette route, les choses iront bientôt mal. Une approche que j'ai trouvée utile dans la pratique pour ne pas avoir de membres de données statiques de classe mais des méthodes d'encapsuleur statique de classe. Ces méthodes peuvent alors contenir l'objet statique en lui-même. Par exemple.

TypeX* Class2::getClass1Instance()
{
    static TypeX obj1;
    return &obj1;
}
</code>

Remarque: Une réponse antérieure dit:

L'autre chose garantie est que l'initialisation des objets statiques à partir d'une unité de traduction sera effectuée avant l'utilisation de tout objet ou fonction de cette unité de traduction

Ce n'est pas tout à fait correct et la norme est incorrectement déduite ici. Cela peut ne pas être vrai si la fonction d'une unité de traduction est appelée avant la saisie de main.

3
Paani

Je pense qu'il est accessible à tout moment pendant l'exécution. Ce qui reste indéfini est l'ordre d'initialisation des variables statiques.

1
erelender

Ils peuvent être initialisés dans un fichier de fichier d'implémentation (.c/cpp/cc). Ne les initialisez pas en .h car le compilateur se plaindra de plusieurs définitions.

Ils sont généralement initialisés avant main, mais l'ordre est inconnu, évitez donc les dépendances. Ils sont certainement accessibles dans la fonction membre. Gardez à l'esprit que l'ordre d'initialisation est inconnu pour les membres statiques. Je suggérerais d'encapsuler un membre statique dans la fonction statique qui vérifiera si le membre a été initialisé.

1
vehomzzz

Il n'y a pas de réponse totalement triviale à cette question, mais fondamentalement, ils sont initialisés juste avant que le contrôle ne soit passé au point d'entrée (principal) de votre programme. L'ordre dans lequel ils sont initialisés est (à ma connaissance) non défini et peut être spécifique au compilateur.

EDIT: Pour clarifier, votre hypothèse ajoutée est correcte. Tant que vous y accédez uniquement après l'entrée principale, vous n'avez pas vraiment à vous soucier de quand/comment il est initialisé. Il sera initialisé d'ici là.

0
Dentoid