J'aime mettre tous mes #includes dans mon fichier d'en-tête, puis inclure uniquement mon en-tête pour ce fichier source dans mon fichier source. Quelle est la norme de l'industrie? Y a-t-il des inconvénients à ma méthode?
En règle générale, vous ne voulez mettre que les inclusions minimales nécessaires dans un fichier d'en-tête de classe, car toute autre personne utilisant cet en-tête sera obligée de #include
tous aussi. Dans les grands projets, cela conduit à des constructions plus lentes, à des problèmes de dépendance et à toutes sortes d'autres problèmes.
Considérez un fichier d'en-tête comme l'interface publique de votre classe. Vous ne voulez pas imposer à tous ceux qui l'utilisent des dépendances supplémentaires, à moins qu'ils ne soient nécessaires pour pouvoir utiliser la classe.
Déplacez tout ce qui n'est nécessaire que dans l'implémentation de la classe vers le bas dans le fichier source. Pour les autres classes utilisées dans un en-tête, uniquement #include
leurs en-têtes si vous avez réellement besoin de connaître leur taille ou leur contenu dans l'en-tête - n'importe quoi d'autre et un déclaration directe est suffisant. Dans la plupart des cas, il vous suffit de #include
les classes dont vous héritez et les classes dont les objets sont des membres de valeur de votre classe.
Cette page a un bon résumé. (Répliqué ci-dessous pour référence)
Les grands projets logiciels nécessitent une gestion minutieuse des fichiers d'en-tête, même lors de la programmation en C. Lorsque les développeurs passent au C++, la gestion des fichiers d'en-tête devient encore plus complexe et prend du temps. Nous présentons ici quelques modèles d'inclusion de fichier d'en-tête qui simplifieront cette tâche.
Ici, nous discutons des règles de base de l'inclusion de fichiers d'en-tête C++ nécessaires pour simplifier la gestion des fichiers d'en-tête.
Un fichier d'en-tête ne doit être inclus que lorsqu'une déclaration directe ne fera pas le travail. Le fichier d'en-tête doit être conçu de manière à ce que l'ordre d'inclusion du fichier d'en-tête ne soit pas important. Pour ce faire, assurez-vous que x.h
est le premier fichier d'en-tête dans x.cpp
Le mécanisme d'inclusion du fichier d'en-tête doit tolérer les inclusions de fichier d'en-tête en double. Les sections suivantes expliquent ces règles à l'aide d'un exemple.
L'exemple suivant illustre différents types de dépendances. Supposons une classe A avec du code stocké dans a.cpp
et a.h
.
a.h
#ifndef _a_h_included_
#define _a_h_included_
#include "abase.h"
#include "b.h"
// Forward Declarations
class C;
class D;
class A : public ABase
{
B m_b;
C *m_c;
D *m_d;
public:
void SetC(C *c);
C *GetC() const;
void ModifyD(D *d);
};
#endif
a.cpp
#include "a.h"
#include "d.h"
void A::SetC(C* c)
{
m_c = c;
}
C* A::GetC() const
{
return m_c;
}
void A::ModifyD(D* d)
{
d->SetX(0);
d->SetY(0);
m_d = d;
}
Permet d'analyser les inclusions du fichier d'en-tête, du point de vue des classes impliquées dans cet exemple, à savoir ABase
, A
, B
, C
et D
.
ABase
est la classe de base, donc la déclaration de classe est requise pour compléter la déclaration de classe. Le compilateur doit connaître la taille de ABase
pour déterminer la taille totale de A
. Dans ce cas abase.h
doit être inclus explicitement dans a.h
.A
contient la classe B
par valeur, donc la déclaration de classe est requise pour compléter la déclaration de classe . Le compilateur doit connaître la taille de B pour déterminer la taille totale de A
. Dans ce cas b.h
doit être inclus explicitement dans a.h
.Class C
n'est inclus que comme référence de pointeur. La taille ou le contenu réel de C
ne sont pas importants pour a.h
ou a.cpp
. Ainsi, seule une déclaration à terme a été incluse dans a.h
. Remarquerez que c.h
n'a été inclus dans aucun des deux a.h
ou a.cpp
.D
est juste utilisée comme référence de pointeur dans a.h
. Une déclaration préalable suffit donc. Mais a.cpp
utilise la classe D
en substance, donc il inclut explicitement d.h
.Les fichiers d'en-tête ne doivent être inclus que lorsqu'une déclaration directe ne fera pas le travail. En n'incluant pas c.h
et d.h
les autres clients de la classe A
n'ont jamais à se soucier de c.h
et d.h
sauf s'ils utilisent les classes C et D par valeur. a.h
a été inclus comme premier fichier d'en-tête dans a.cpp
Cela garantira que a.h
ne s'attend pas à ce qu'un certain fichier d'en-tête soit inclus avant a.h
. Comme a.h
a été inclus comme premier fichier, compilation réussie de a.cpp
fera en sorte que a.h
ne s'attend pas à ce qu'un autre fichier d'en-tête soit inclus avant a.h
. Si cela est suivi pour toutes les classes, (c'est-à-dire x.cpp
comprend toujours x.h
comme premier en-tête) il n'y aura aucune dépendance à l'inclusion du fichier d'en-tête. a.h
inclut la vérification de la définition du symbole du préprocesseur _a_h_included_
. Cela le rend tolérant de dupliquer des inclusions de a.h
.
Une dépendance cyclique existe entre la classe X
et Y
dans l'exemple suivant. Cette dépendance est gérée à l'aide de déclarations avancées.
x.h and y.h
/* ====== x.h ====== */
// Forward declaration of Y for cyclic dependency
class Y;
class X
{
Y *m_y;
...
};
/* ====== y.h ====== */
// Forward declaration of X for cyclic dependency
class X;
class Y
{
X *m_x;
...
};