L’utilisation de double include garde en C ++

J’ai donc récemment eu une discussion où je travaille, dans laquelle je me posais la question de l’utilisation d’une garde double include sur un seul garde. Ce que je veux dire par double garde est la suivante:

Fichier d’en-tête “header_a.hpp”:

#ifndef __HEADER_A_HPP__ #define __HEADER_A_HPP__ ... ... #endif 

Lorsque vous incluez le fichier d’en-tête n’importe où, soit dans un en-tête ou un fichier source:

 #ifndef __HEADER_A_HPP__ #include "header_a.hpp" #endif 

Maintenant, je comprends que l’utilisation de la garde dans les fichiers d’en-tête est d’empêcher l’inclusion multiple d’un fichier d’en-tête déjà défini, c’est commun et bien documenté. Si la macro est déjà définie, l’intégralité du fichier d’en-tête est considérée comme «vierge» par le compilateur et la double inclusion est empêchée. Assez simple.

Le problème que je ne comprends pas est d’utiliser #ifndef __HEADER_A_HPP__ et #endif autour du #include "header_a.hpp" . Le collègue me dit que cela ajoute une deuxième couche de protection aux inclusions mais je ne vois pas comment cette deuxième couche est même utile si la première couche fait absolument le travail (ou le fait-elle?).

Le seul avantage que je puisse trouver est qu’il empêche carrément l’éditeur de liens de trouver le fichier. S’agit-il d’améliorer le temps de compilation (qui n’a pas été mentionné comme un avantage), ou y a-t-il autre chose au travail que je ne vois pas?

Je suis à peu près certain que l’ajout d’un autre garde d’inclusion est une mauvaise pratique:

 #ifndef __HEADER_A_HPP__ #include "header_a.hpp" #endif 

Voici quelques raisons:

  1. Pour éviter la double inclusion, il suffit d’append un garde d’inclusion habituel dans le fichier d’en-tête lui-même. Cela fait bien le travail. Un autre garde d’inclusion à l’endroit de l’inclusion ne fait que gâcher le code et réduit la lisibilité.

  2. Il ajoute des dépendances inutiles. Si vous changez include guard à l’intérieur du fichier d’en-tête, vous devez le changer à tous les endroits où l’en-tête est inclus.

  3. Ce n’est certainement pas l’opération la plus coûteuse comparant l’ensemble du processus de compilation / couplage, de sorte qu’il ne peut guère réduire le temps de construction total.

  4. Tout compilateur valable pour quelque chose optimise déjà les inclus-gardes à l’échelle du fichier .

La raison pour laquelle vous mettez des gardes d’inclusion dans le fichier d’en- tête est d’empêcher que le contenu de l’en-tête ne soit extrait plus d’une fois dans une unité de traduction. C’est une pratique normale et établie depuis longtemps.

La raison pour laquelle les gardes d’inclusion redondantes sont placées dans un fichier source est d’éviter d’avoir à ouvrir le fichier d’en-tête qui est inclus, et de le faire dans les jours anciens, ce qui pourrait accélérer considérablement la compilation. De nos jours, l’ouverture d’un fichier est beaucoup plus rapide qu’auparavant; de plus, les compilateurs sont assez intelligents pour se souvenir des fichiers qu’ils ont déjà vus, et ils comprennent les idiomes de garde inclus, donc ils peuvent déterminer eux-mêmes qu’ils n’ont pas besoin d’ouvrir le fichier à nouveau. C’est un peu la main, mais l’essentiel est que cette couche supplémentaire n’est plus nécessaire.

EDIT: un autre facteur est que la compilation de C ++ est beaucoup plus compliquée que la compilation de C, donc cela prend beaucoup plus de temps, rendant le temps d’ouverture des fichiers plus petit, moins important, pour comstackr une unité de traduction.

Le seul avantage que je puisse trouver est qu’il empêche carrément l’éditeur de liens de trouver le fichier.

L’éditeur de liens ne sera en aucun cas affecté.

Cela pourrait empêcher le pré-processeur de chercher le fichier, mais si le garde est défini, cela signifie qu’il a déjà trouvé le fichier. Je soupçonne que si le temps de pré-traitement est réduit du tout, l’effet serait tout à fait minime, sauf dans la monstruosité incluse de manière la plus récurrente du sharepoint vue pathologique.

L’inconvénient est que si la garde est changée (par exemple en raison d’un conflit avec un autre garde), toutes les conditions avant les directives d’inclusion doivent être modifiées pour qu’elles fonctionnent. Et si quelque chose d’autre utilise le garde précédent, les conditions doivent être modifiées pour que la directive include fonctionne correctement.

PS __HEADER_A_HPP__ est un symbole réservé à l’implémentation, ce n’est donc pas quelque chose que vous pouvez définir. Utilisez un autre nom pour le garde.

Les anciens compilateurs sur les plates-formes plus traditionnelles (les ordinateurs centraux) (nous parlons ici du milieu des années 2000) n’avaient pas l’habitude d’avoir l’optimisation décrite dans d’autres réponses, et donc ils ralentissaient considérablement le temps de prétraitement. qui ont déjà été inclus (en gardant à l’esprit un grand projet monolithique d’entreprise-y, vous allez inclure BEAUCOUP de fichiers d’en-tête). À titre d’exemple, j’ai vu des données indiquant une accélération de 26 fois pour un fichier avec 256 fichiers d’en-tête contenant chacun les mêmes 256 fichiers d’en-tête sur le compilateur VisualAge C ++ 6 pour AIX (datant du milieu des années 2000). C’est un exemple plutôt extrême, mais ce genre d’accélération s’accumule.

Cependant, tous les compilateurs modernes, même sur les plates-formes mainframe telles que AIX et Solaris, effectuent suffisamment d’optimisation pour l’inclusion des en-têtes, si bien que la différence actuelle est vraiment négligeable. Il n’y a donc pas de bonne raison de les avoir plus.

Cela explique toutefois pourquoi certaines entresockets se sont toujours accrochées à la pratique car, relativement récemment (du moins en termes de base de code C / C ++), cela valait toujours la peine pour de très grands projets monolithiques.

Bien qu’il y ait des gens qui s’y opposent, en pratique “#pragma once” fonctionne parfaitement et les compilateurs principaux (gcc / g ++, vc ++) le supportent.

Donc, quelle que soit l’argumentation puriste que les gens diffusent, cela fonctionne beaucoup mieux:

  1. Vite
  2. Pas d’entretien, pas de problème avec la mystérieuse non-inclusion car vous avez copié un ancien drapeau
  3. Une seule ligne avec une signification évidente versus des lignes cryptiques réparties dans un fichier

Donc, mettez simplement:

 #pragma once 

au début du fichier, et c’est tout. Optimisé, maintenable et prêt à l’emploi.