7
votes

C ++ en-têtes - Meilleure pratique lorsque vous y compris

Les en-têtes C ++

Si j'ai A.CPP et AH ainsi que BH, CH, DH P>

Dois-je faire: P>

dans AH: P> xxx pré>

en A.CPP: p> xxx pré>

ou b> p>

en A.CPP: P>

#include "A.h"
#include "b.h"
#include "c.h"
#include "d.h"


0 commentaires

6 Réponses :


0
votes

Préfère non inclure les en-têtes dans d'autres en-têtes - il ralentit la compilation et les lées à une référence circulaire


2 commentaires

@MGD - Si j'ai une en-tête qui compte 10 inclut et que la source était différente, devrions-je simplement déplacer l'inclinaison de l'en-tête à la source afin que la source ait 20 inclus?


Voir la réponse plus complète d'Alex. Une exception à cet égard est une exception si vous avez des en-têtes précompilés, vous incluez dans ce cas toutes les en-têtes de plate-forme / cadre / système d'exploitation qui ne changeront jamais dans un seul fichier et incluent que partout



0
votes

Il n'y aurait pas de problèmes de performance, tout cela fait à la compilation, et vos en-têtes doivent être configurés afin qu'ils ne puissent pas être inclus plus d'une fois.

Je ne sais pas s'il y a une façon standard de le faire, mais je préfère inclure tous les en-têtes dont j'ai besoin dans des fichiers source et les inclure dans les en-têtes si quelque chose dans l'en-tête en a besoin (par exemple, un TapeDef d'un autre en-tête)


1 commentaires

@Jeff - Ah Droite, les articles de l'en-tête pourraient en avoir besoin. Mais ne pourraient-ils pas l'obtenir si tous les en-têtes sont dans le fichier source?



24
votes

Vous ne devez inclure que ce qui est nécessaire pour compiler; L'ajout d'une inclusion inutile vous fera mal à vos temps de compilation, en particulier dans de grands projets.

Chaque fichier d'en-tête doit pouvoir compiler proprement à sa propre position - c'est-à-dire que si vous avez un fichier source qui n'inclut que cet en-tête, il devrait compiler sans les erreurs. Le fichier d'en-tête ne doit pas inclure plus que nécessaire pour cela. P>

Essayez d'utiliser autant que possible les déclarations avant que possible. Si vous utilisez une classe, mais le fichier d'en-tête ne traite que des pointeurs / références aux objets de cette classe, il n'est pas nécessaire d'inclure la définition de la classe - utilisez simplement une déclaration en avant: P>

class SomeClass;
// Can now use pointers/references to SomeClass
// without needing the full definition


6 commentaires

@Adam - Avez-vous un exemple de déclarations avant et quand il est approprié d'utiliser et quand non?


Très bon conseil. Je recommanderais également que le fichier .C associé à un .h inclut le .h comme le premier inclus, toujours. C'est pour vous assurer que tous vos fichiers .h peuvent toujours compiler eux-mêmes (au lieu de s'appuyer sur l'ordre incluant dans votre fichier .C).


+1 Pour la dernière partie, j'ai déjà toujours inclus le fichier d'en-tête.


Vous pouvez également utiliser une déclaration à terme si vous retournez soméclass par valeur.


Les demandeurs font également référence à @Frerichraabe Réponse ci-dessous.


Oui, le fichier d'en-tête lui-même devrait être autonome. Il devrait contenir toutes les dépendances dont il a besoin.



3
votes

Quel Adam Rosenfield vous a dit que vous êtes sur place. Un exemple de lorsque vous pouvez utiliser une déclaration à terme est la suivante:

#ifndef A_H_
#define A_H_

    #include "D.h"
    class B;  //forward declaration
    class C;  //forward declaration

    class A
    {
    B *m_pb;  //you can forward declare this one bacause it's a pointer and the compilier doesn't need to know the size of object B at this point in the code.  include B.h in the cpp file.
    C &m_rc;  //you can also forware declare this one for the same reason, except it's a reference.

    D m_d;    //you cannot forward declare this one because the complier need to calc the size of object D.

    };

#endif


1 commentaires

(J'espère que cela s'appuie, je ne l'ai pas testé lol. Faites-moi savoir si vous avez un problème avec cela).



6
votes

La pratique clé ici est d'avoir autour de chaque fichier foo.h un protège tel que: xxx

Cela empêche les inclusions multiples, avec des boucles et toutes ces horreurs de ce standard. Une fois que vous assurez que chaque fichier Include est ainsi gardé, les spécificités sont moins importantes.

J'aime un principe de guidage Adam Exprime: Assurez-vous que si un fichier source comprend simplement ah , il n'aura pas inévitablement des erreurs dus à ah supposer d'autres fichiers ont été incluses avant - par exemple Si ah nécessite BH pour être inclus auparavant, il peut et devrait simplement inclure BH même (les gardes feront une noopité si bh était déjà Inclus précédemment)

Vice versa, un fichier source doit inclure les en-têtes à partir desquels il nécessite quelque chose (macros, déclarations, etc.), pas supposons que d'autres en-têtes viennent de passer par magie Parce que cela en a inclus certains.

Si vous utilisez des cours par valeur, hélas, vous avez besoin de tous les détails de la classe de la classe dans certains .h vous inclut. Mais pour certaines utilisations via des références ou des pointeurs, juste un SIC de classe nue; suffira. Par exemple, toutes les autres choses étant égales, si la classe A peut vous échapper avec un pointeur à une instance de classe B (c.-à-d. Un membre classe B * my_bp; plutôt qu'un membre plutôt qu'un membre classe b * my_b; ) Le couplage entre les fichiers inclus peut être faiblement affaibli (réduisant beaucoup de recompilation) - par exemple bh pourrait avoir un peu plus que la classe B; pendant que tous les détails de gory sont dans b_impl.h qui est inclus uniquement par les en-têtes que vraiment besoin de ...


2 commentaires

Étant donné que foo.h sera toujours lu à chaque fois qu'il est inclus, ce n'est pas vraiment un non-op. Ce serait un non-op si vous l'avez fait: #Ifndef _foo_h #include "foo.h" #undif


Ouais, mais cela polluerait toute inclusion dans la non-réadapabilité. À toutes fins utiles et à des fins multiples figurent dans le cache du système de fichiers. Il n'y a donc pas de performance dans "y compris" "y compris" et il s'agit certainement d'un non-op du point de vue de la sémantique, ce qui est ce qui est vraiment questions.



3
votes

Réponse: Laissez ah Inclure BH , ch et dh uniquement s'il est nécessaire de faire réussir la construction . La règle de base est la suivante: n'incluez que autant de code dans un fichier d'en-tête si nécessaire. Tout ce qui n'est pas immédiatement nécessaire dans ah doit être inclus par A.CPP .

raisonnement: le code MOINS est inclus dans les fichiers d'en-tête, moins vous aurez probablement besoin de Pour recompiler le code qui utilise le fichier d'en-tête après avoir changé quelque part. S'il y a beaucoup de #include des références entre différents fichiers d'en-tête, la modification de l'un d'entre eux nécessitera une reconstruction tous les fichiers qui incluent le fichier d'en-tête modifié - Récursel. Donc, si vous décidez de toucher un fichier d'en-tête Toplevel, cela pourrait reconstruire d'énormes parties de votre code.

en utilisant Déclarations avant Dans la mesure du possible dans vos fichiers d'en-tête, vous réduisez le couplage des fichiers source et rendez ainsi la construction plus rapide. Ces déclarations à terme peuvent être utilisées dans de nombreuses situations que vous pourriez penser. En règle générale, vous n'avez besoin que du fichier d'en-tête t.h (qui définit un type t ) si vous

  1. Déclarez une variable de membre de type t (Remarque, ce n'est pas notamment déclarant un pointeur à- t ).
  2. Écrivez certaines fonctions en ligne qui accèdent aux membres d'un objet de type t .

    vous faites pas besoin d'inclure la déclaration de t si votre fichier d'en-tête juste

    1. déclare des constructeurs / fonctions qui prennent des références ou des pointeurs sur un objet t .
    2. déclare des fonctions qui renvoient un objet t par le pointeur, la référence ou la valeur.
    3. déclare les variables des membres qui sont des références ou des pointeurs vers un objet t .

      considère cette déclaration de classe; Lequel des fichiers inclus pour A , b et c Avez-vous vraiment besoin d'inclure?: xxx

      Vous avez juste besoin du fichier incluant pour le type C , en raison de la variable de membre m_c . Vous pouvez souvent supprimer cette exigence également en ne déclarant pas vos variables de membre directement, mais utilisez un Pointeur opaque pour masquer toutes les variables de membre dans une structure privée, de sorte qu'elles ne se présentent plus dans le fichier d'en-tête.


3 commentaires

Vous mélangez A, B et C avec X, Y et Z dans votre exemple.


@Bill: Vous avez raison, j'ai ajusté l'exemple pour dire A, B, c maintenant.


@Frerichraabe réponse parfaite. +1 pour le "raisonnement:" partie.