Où mettre les déclarations, l'en-tête ou la source?

107

Dois-je mettre les inclusions dans le fichier d'en-tête ou le fichier source? Si le fichier d'en-tête contient les instructions d'inclusion, alors si j'inclus ce fichier d'en-tête dans ma source, mon fichier source aura-t-il tous les fichiers inclus qui étaient dans mon en-tête? Ou devrais-je les inclure uniquement dans mon fichier source?

Mohit Deshpande
la source
2
Beaucoup de doublons précédents sur SO, par exemple où "inclure" devrait être mis en C ++
Paul R

Réponses:

141

Ne mettez des inclus dans un en-tête que si l'en-tête lui-même en a besoin.

Exemples:

  • Votre fonction renvoie le type size_t. Puis #include <stddef.h>dans le fichier d' en- tête .
  • Votre fonction utilise strlen. Puis #include <string.h>dans le fichier source .
schot
la source
2
Et si ma fonction prend un argument de type size_t?
andrybak
Même question en extension vers C ++: et si ma structure / classe a un champ / membre de type size_tou std::string?
andrybak
10
Quelle est la justification?
Patrizio Bertoni
J'ai une situation câblée, la classe C ++ A a un objet d'une autre classe B, et je ne peux pas utiliser la déclaration avant de B et la fin y compris l'en-tête B dans l'en-tête A. (l'utilisation du pointeur n'a pas ce problème)
shuva
@andrybak Vos fichiers source doivent inclure votre fichier d'en-tête afin que toute inclusion de votre en-tête récupère votre source.
Jeremy Trifilo
27

Il y a eu pas mal de désaccord à ce sujet au fil des ans. À un moment donné, il était traditionnel qu'un en-tête ne déclare que ce qui était dans le module auquel il était lié, tant d'en- têtes avaient des exigences spécifiques que vous #includeun certain ensemble d'en-têtes (dans un ordre spécifique). Certains programmeurs C extrêmement traditionnels suivent encore ce modèle (religieusement, dans au moins certains cas).

Plus récemment, il y a eu un mouvement pour rendre la plupart des en-têtes autonomes. Si cet en-tête nécessite autre chose, l'en-tête lui-même gère cela, en s'assurant que tout ce dont il a besoin est inclus (dans le bon ordre, s'il y a des problèmes de commande). Personnellement, je préfère cela - surtout lorsque l'ordre des en-têtes peut être important, cela résout le problème une fois, au lieu de demander à tous ceux qui l'utilisent de résoudre le problème une fois de plus.

Notez que la plupart des en-têtes ne doivent contenir que des déclarations. Cela signifie que l'ajout d'un en-tête inutile ne devrait (normalement) pas avoir d'effet sur votre exécutable final. Le pire qui arrive est que cela ralentit un peu la compilation.

Jerry Coffin
la source
2
Si tous les en-têtes sont écrits dans le second style, il ne devrait y avoir aucun problème d'ordre. Des problèmes de commande dans les en-têtes signifient généralement que vous n'avez pas inclus tout ce dont vous avez besoin dans l'en-tête.
Au revoir SE
12

Vos #includes doivent être des fichiers d'en-tête, et chaque fichier (source ou en-tête) doit #includecontenir les fichiers d'en-tête dont il a besoin. Les fichiers d'en-tête doivent contenir #includele minimum de fichiers d'en-tête nécessaire, et les fichiers source devraient également, bien que ce ne soit pas aussi important pour les fichiers source.

Le fichier source aura les en-têtes qu'il #includes, et les en-têtes eux #include, et ainsi de suite jusqu'à la profondeur d'imbrication maximale. C'est pourquoi vous ne voulez pas de #includes superflus dans les fichiers d'en-tête: ils peuvent amener un fichier source à inclure beaucoup de fichiers d'en-tête dont il n'a peut-être pas besoin, ce qui ralentit la compilation.

Cela signifie qu'il est tout à fait possible que les fichiers d'en-tête soient inclus deux fois, et cela peut être un problème. La méthode traditionnelle consiste à mettre «inclure les gardes» dans les fichiers d'en-tête, comme celui-ci pour le fichier foo.h:

#ifndef INCLUDE_FOO_H
#define INCLUDE_FOO_H
/* everything in header goes here */
#endif
David Thornley
la source
Je sais que cette réponse est très ancienne, mais depuis lors, ils ont ajouté #pragma une fois, donc vous ne devez pas inclure #ifndef lors de la déclaration #includes J'ai publié ce car les fils plus anciens mais plus populaires / avec des votes positifs ont tendance à être en haut des recherches Google
Dogunbound hounds le
6

L'approche dans laquelle j'ai évolué en vingt ans est la suivante;

Prenons une bibliothèque.

Il existe plusieurs fichiers C, un fichier H interne et un fichier H externe. Les fichiers C incluent le fichier H interne. Le fichier H interne comprend le fichier H externe.

Vous voyez que dans le PDV des compilateurs, comme il compile un fichier C, il y a une hiérarchie;

externe -> interne -> code C

C'est le bon ordre, car ce qui est externe est tout ce dont un tiers a besoin pour utiliser la bibliothèque. Ce qui est interne est nécessaire pour compiler le code C.


la source
4

Si le fichier d' en- tête Un #includesfichiers en- tête B et C, puis chaque fichier source #includesA sera également obtenir B et C #included. Le pré-processeur effectue littéralement une substitution de texte: partout où il trouve du texte indiquant #include <foo.h>qu'il le remplace par le texte du foo.hfichier.

Il existe différentes opinions quant à savoir si vous devez insérer des #includesen-têtes ou des fichiers source. Personnellement, je préfère tout mettre #includesdans le fichier source par défaut, mais tous les fichiers d'en-tête qui ne peuvent pas être compilés sans d'autres en-têtes prérequis devraient #includeces en-têtes eux-mêmes.

Et chaque fichier d'en-tête doit contenir une garde d'inclusion pour éviter qu'il ne soit inclus plusieurs fois.

Vicky
la source
4

Dans certains environnements, la compilation sera la plus rapide si l'on n'inclut que les fichiers d'en-tête dont on a besoin. Dans d'autres environnements, la compilation sera optimisée si tous les fichiers source peuvent utiliser la même collection principale d'en-têtes (certains fichiers peuvent avoir des en-têtes supplémentaires au-delà du sous-ensemble commun). Idéalement, les en-têtes doivent être construits de sorte que plusieurs opérations #include n'aient aucun effet. Il peut être bon d'entourer les instructions #include de vérifications pour l'inclusion-guard du fichier à inclure, bien que cela crée une dépendance sur le format de cette garde. De plus, en fonction du comportement de mise en cache des fichiers d'un système, une #inclusion inutile dont la cible finit par être complètement # ifdef'ed ne peut pas prendre longtemps.

Une autre chose à considérer est que si une fonction prend un pointeur vers une structure, on peut écrire le prototype comme

void foo (struct BAR_s * bar);

sans qu'une définition pour BAR_s soit dans la portée. Une approche très pratique pour éviter les inclusions inutiles.

PS - dans beaucoup de mes projets, il y aura un fichier que chaque module devrait #include, contenant des choses comme des typedefs pour des tailles entières et quelques structures et unions courantes [par exemple

typedef union {
  non signé long l;
  lw court non signé [2];
  non signé char lb [4];
} U_QUAD;

(Oui, je sais que j'aurais des problèmes si je passais à une architecture big-endian, mais comme mon compilateur n'autorise pas les structures anonymes dans les unions, l'utilisation d'identificateurs nommés pour les octets dans l'union nécessiterait qu'ils soient accessibles comme theUnion.b.b1 etc. qui semble plutôt ennuyeux.

supercat
la source
3

Créez tous vos fichiers afin qu'ils puissent être créés en utilisant uniquement ce qu'ils contiennent. Si vous n'avez pas besoin d'une inclusion dans votre en-tête, supprimez-la. Dans un grand projet, si vous ne maintenez pas cette discipline, vous vous laissez ouvert à la rupture d'une compilation entière lorsque quelqu'un supprime une inclusion d'un fichier d'en-tête qui est utilisé par un consommateur de ce fichier et même pas par l'en-tête.

rediffusion
la source
1

Votre fichier source aura les instructions d'inclusion si vous le mettez dans l'en-tête. Cependant, dans certains cas, il serait préférable de les mettre dans le fichier source.

N'oubliez pas que si vous incluez cet en-tête dans d'autres sources, ils obtiendront également les inclusions de l'en-tête, ce qui n'est pas toujours souhaitable. Vous ne devez inclure les éléments que là où ils sont utilisés.

Splashdust
la source
1

Vous ne devez inclure dans votre en-tête que les fichiers dont vous avez besoin pour déclarer des constantes et des déclarations de fonction. Techniquement, ces inclusions seront également incluses dans votre fichier source, mais par souci de clarté, vous ne devez inclure dans chaque fichier que les fichiers que vous devez réellement utiliser. Vous devez également les protéger dans votre en-tête de l'inclusion multiple ainsi:

#ifndef NAME_OF_HEADER_H
#define NAME_OF_HEADER_H

...definition of header file...

#endif

Cela empêche l'en-tête d'être inclus plusieurs fois, ce qui entraîne une erreur du compilateur.

JRam930
la source