11
votes

C ++ Statique Global NON-POD: Théorie et pratique

Je lisais les Documents de conventions de codage QT et je suis venu au paragraphe suivant:

Tout ce qui a un constructeur ou un besoin à exécuter le code à initialiser ne peut pas être utilisé comme objet global dans le code de la bibliothèque, car il est indéfini lorsque ce constructeur / code sera exécuté (sur la première utilisation, sur la charge de la bibliothèque, avant la charge de la bibliothèque, ) ou pas du tout ). Même si le temps d'exécution de l'initialiseur est défini pour les bibliothèques partagées, vous aurez des problèmes lors de la déplacement de ce code dans un plugin ou si la bibliothèque est compilée de manière statique.

Je sais ce que le Théorie dit, mais je n'ai pas t comprendre la partie "pas du tout". Parfois, j'utilise les statiques de non-Pod Global Const (E.G: Qstring) et il n'est jamais venu pour moi qu'ils ne pourraient pas être initialisés ... est-ce spécifique aux objets / dlls partagés? Cela se produit-il uniquement pour les compilateurs cassés?

Que pensez-vous de cette règle?


1 commentaires

Votre QString peut ne pas être initialisé si vous ne les utilisez pas. Mais si vous les utilisez, ils seront initialisés. Même si c'est juste avant l'utilisation (c.-à-d. Juste avant que le mthod ne soit appelé sur l'objet).


5 Réponses :


15
votes

La partie "non du tout" dit simplement que la norme C ++ est silencieuse à propos de ce problème. Il ne sait pas sur les bibliothèques partagées et ne dit donc rien de l'interaction de certaines fonctionnalités C ++ avec celles-ci.

En pratique, j'ai vu des globaux statiques mondiaux non-Pod utilisés sur Windows, OSX et de nombreuses versions de Linux et d'autres non-unes, dans les programmes de lignes d'interface graphique et de commande, en tant que plugins et en tant qu'applications autonomes. Au moins un projet (qui a utilisé des globaux statiques non-Pod) avait des versions pour l'ensemble complet de toutes les combinaisons de ces combinaisons. Le seul problème que j'ai jamais vu était que une très vieille version de GCC a généré un code appelé les dtors de tels objets dans des bibliothèques dynamiques lorsque l'exécutable s'est arrêté, non pas lorsque la bibliothèque a été déchargée. Bien sûr, c'était fatal (le code de la bibliothèque a été appelé lorsque la bibliothèque était déjà partie), mais cela a été presque une décennie il y a presque 10 ans.

Mais bien sûr, cela ne garantit toujours rien.


0 commentaires

2
votes

Je ne pense pas que les constructeurs d'objets statiques puissent être élus. Il y a probablement une confusion avec le fait qu'une bibliothèque statique est souvent juste un tas d'objets qui sont des jetons dans l'exécutable si ils sont référencés. Certains objets statiques sont conçus de manière à ne être référencés en dehors de leur objet contenant, et le fichier d'objet est donc mis dans l'exécutable uniquement s'il existe une autre dépendance. Ce n'est pas le cas dans certains modèles (à l'aide d'un objet statique qui s'inscrit par exemple).


3 commentaires

Je pense que vous confondez "objet statique" avec "bibliothèque statique".


Par "Objets statiques", je voulais dire "des objets avec durée de stockage statique". BTW, j'ai trouvé la référence indiquant qu'ils ne peuvent pas être supprimés si leur initialisation ou leur destructeur a un effet secondaire: 3.7.1 / 2.


Je voulais dire que les bibliothèques statiques ne sont probablement pas une question ici. Ce sont des bibliothèques dynamiques qui sont le problème.



4
votes

Si l'objet statique est défini dans un objet qui n'est pas référencé, le linker peut tailler complètement l'objet, y compris le code d'initialisation statique. Il sera le faire régulièrement pour les Libs (c'est ainsi que la LIBC ne se lie pas complètement lors de l'utilisation des parties de celui-ci sous GNU, par exemple.

Fait intéressant, je ne pense pas que cela soit spécifique aux bibliothèques. Cela peut probablement arriver pour des objets même dans la construction principale.


0 commentaires

3
votes

Je ne vois aucun problème avec des objets globaux avec des constructeurs.

Ils ne devraient pas avoir de dépendance sur d'autres objets globaux de leur constructeur (ou destructeur).

Mais si elles ont des dépendances, l'objet dépendant doit soit par la même unité de compilation, soit être évalué paresseusement afin que vous puissiez le forcer à être évalué avant de l'utiliser.

Le code du constructeur ne devrait pas non plus être dépendant de la date à laquelle (ceci est lié aux dépendances mais pas tout à fait la même chose) il est exécuté, mais vous êtes en sécurité pour supposer qu'il sera construit à tout le moins (juste avant un La méthode est appelée) et C ++ garantit l'ordre de destruction est l'inverse de l'instanciation.

Ce n'est pas si difficile à coller à ces règles.


0 commentaires

2
votes

C ++ ne définit pas la commande selon laquelle les initialisateurs statiques exécutés pour des objets dans différentes compilations (la commande est bien définie dans une unité de compilation).

Considérez la situation où vous avez 2 objets statiques A et B définis dans différentes unités de compilation. Disons que cet objet B utilise effectivement l'objet A dans sa première initialisation.

Dans ce scénario, il est possible que B soit initialisé en premier et appelle un appel à un objet ininitialisé. Cela pourrait être une chose qui est signifiée par "pas du tout" - un objet est utilisé lorsqu'il n'a pas eu l'occasion de l'initialiser d'abord (même si cela pourrait être initialisé plus tard).

Je suppose que la liaison dynamique pourrait ajouter des complexités que je n'ai pas pensées à ce cuase un objet de ne jamais être initialisé. Dans les deux cas, le résultat final est que l'initializatino statique introduit suffisamment de problèmes potentiels qu'il devrait être évité dans la mesure du possible et très soigneusement manipulé où vous devez l'utiliser.


0 commentaires