Lorsque vous divisez votre code en plusieurs fichiers, que faut-il exactement dans un fichier .h et dans un fichier .cpp?
Fichiers d'en-tête (.h
) sont conçus pour fournir les informations nécessaires dans plusieurs fichiers. Des choses comme les déclarations de classe, les prototypes de fonctions et les énumérations sont généralement insérés dans les fichiers d'en-tête. Dans un mot, "définitions".
Fichiers de code (.cpp
) sont conçus pour fournir les informations de mise en oeuvre qui ne doivent être connues que dans un seul fichier. En général, les corps de fonction et les variables internes auxquelles d'autres modules n'auront jamais accès, appartiennent à .cpp
des dossiers. Dans un mot, "implémentations".
La question la plus simple à vous poser vous-même pour déterminer ce qui appartient est "si je change cela, devrai-je changer le code dans d'autres fichiers pour que les choses soient à nouveau compilées?" Si la réponse est "oui", il appartient probablement au fichier d'en-tête; si la réponse est "non", il appartient probablement au fichier de code.
En réalité, en C++, ceci est un peu plus compliqué que l’organisation en-tête/source C.
Le compilateur voit un gros fichier source (.cpp) avec ses en-têtes correctement inclus. Le fichier source est l'unité de compilation qui sera compilée dans un fichier objet.
Parce qu'une unité de compilation peut avoir besoin d'informations sur une implémentation dans une autre unité de compilation. Ainsi, on peut écrire par exemple l'implémentation d'une fonction dans une source et écrire la déclaration de cette fonction dans une autre source nécessitant de l'utiliser.
Dans ce cas, il existe deux copies de la même information. Quel est le mal ...
La solution consiste à partager quelques détails. Tandis que l'implémentation doit rester dans la source, la déclaration de symboles partagés, tels que des fonctions, ou la définition de structures, de classes, d'énums, etc., devra peut-être être partagée.
Les en-têtes sont utilisés pour mettre ces détails partagés.
Déplacer vers l'en-tête les déclarations de ce qui doit être partagé entre plusieurs sources
En C++, d'autres éléments pourraient être placés dans l'en-tête, car ils doivent également être partagés:
Déplacer vers l'en-tête TOUT ce qui doit être partagé, y compris les implémentations partagées
Oui. En fait, il y a beaucoup de choses différentes qui pourraient être dans un "en-tête" (c'est-à-dire partagé entre des sources).
Cela devient compliqué, et dans certains cas (dépendances circulaires entre les symboles), impossible de le conserver dans un en-tête.
Cela signifie que, dans un cas extrême, vous pourriez avoir:
Imaginons que nous ayons un MyObject basé sur un modèle. Nous pourrions avoir:
// - - - - MyObject_forward.hpp - - - -
// This header is included by the code which need to know MyObject
// does exist, but nothing more.
template<typename T>
class MyObject ;
.
// - - - - MyObject_declaration.hpp - - - -
// This header is included by the code which need to know how
// MyObject is defined, but nothing more.
#include <MyObject_forward.hpp>
template<typename T>
class MyObject
{
public :
MyObject() ;
// Etc.
} ;
void doSomething() ;
.
// - - - - MyObject_implementation.hpp - - - -
// This header is included by the code which need to see
// the implementation of the methods/functions of MyObject,
// but nothing more.
#include <MyObject_declaration.hpp>
template<typename T>
MyObject<T>::MyObject()
{
doSomething() ;
}
// etc.
.
// - - - - MyObject_source.cpp - - - -
// This source will have implementation that does not need to
// be shared, which, for templated code, usually means nothing...
#include <MyObject_implementation.hpp>
void doSomething()
{
// etc.
} ;
// etc.
Dans la "vraie vie", c'est généralement moins compliqué. La plupart du code n'aura qu'un simple en-tête/une organisation source, avec du code en ligne dans le source.
Mais dans d’autres cas (objets modèles se connaissant mutuellement), je devais avoir pour chaque objet des en-têtes de déclaration et d’implémentation distincts, avec une source vide comprenant ces en-têtes, juste pour m'aider à voir certaines erreurs de compilation.
Une autre raison de diviser les en-têtes en différents en-têtes pourrait être d'accélérer la compilation, en limitant la quantité de symboles analysés au strict nécessaire et en évitant une recompilation inutile d'une source qui ne se soucie que de la déclaration anticipée lorsqu'une implémentation de méthode en ligne a changé.
Vous devez rendre votre organisation du code aussi simple que possible et aussi modulaire que possible. Mettez autant que possible dans le fichier source. N'exposez que dans les en-têtes ce qui doit être partagé.
Mais le jour où vous aurez des dépendances circulaires entre des objets basés sur des modèles, ne soyez pas surpris si votre organisation de code devient un peu plus "intéressante" que l'organisation en-tête/source simple ...
^ _ ^
en plus de toutes les autres réponses, je vous dirai ce que vous ne placez pas dans un fichier d'en-tête:using
déclaration (le plus courant étant using namespace std;
) ne doit pas apparaître dans un fichier d’en-tête car ils polluent l’espace de noms du fichier source dans lequel il est inclus.
Qu'est-ce que compile en rien (zéro empreinte binaire) va dans le fichier d'en-tête.
Les variables ne compilent pas en rien, mais les déclarations de type le font (car elles décrivent uniquement le comportement des variables).
les fonctions ne le font pas, mais les fonctions en ligne le font (ou les macros), car elles ne produisent du code que là où elles sont appelées.
les modèles ne sont pas du code, ils ne sont qu'une recette pour créer du code. alors ils vont aussi dans h fichiers.
En général, vous placez des déclarations dans le fichier d'en-tête et des définitions dans le fichier d'implémentation (.cpp). L'exception à cette règle concerne les modèles, où la définition doit également figurer dans l'en-tête.
Cette question et d’autres similaires à celle-ci ont été posées fréquemment sur SO - voir Pourquoi avoir des fichiers d’en-tête et des fichiers .cpp en C++? et Fichiers d’en-tête C++ , Séparation du code par exemple.
Vos déclarations de classe et de fonction, ainsi que la documentation et les définitions des fonctions/méthodes inline (bien que certains préfèrent les placer dans des fichiers .inl distincts).
Le fichier d’entête contient principalement classe skeleton ou déclaration (ne change pas souvent)
et le fichier cpp contient implémentation de classe (change fréquemment).
le fichier d'en-tête (.h) doit être utilisé pour les déclarations de classes, les structures et leurs méthodes, les prototypes, etc. L'implémentation de ces objets est faite en cpp.
en .h
class Foo {
int j;
Foo();
Foo(int)
void DoSomething();
}
Je m'attendrais à voir:
la vraie réponse est cependant de ne pas mettre dans:
En-tête (.h)
Corps (.cpp)
En règle générale, vous placez la partie "partagée" du module sur le .h (la partie que les autres modules doivent pouvoir voir) et la partie "non partagée" sur le .cpp.
PD: Oui, j'ai inclus les variables globales. Je les ai utilisées quelques fois et il est important de ne pas les définir sur les en-têtes, sinon vous aurez beaucoup de modules, chacun définissant sa propre variable.
EDIT: Modifié après le commentaire de David
L'en-tête définit quelque chose mais ne dit rien de l'implémentation. (Exclure les modèles dans ce "metafore".
Cela dit, vous devez diviser les "définitions" en sous-groupes. Il existe dans ce cas deux types de définitions.
Maintenant, je parle bien sûr du premier sous-groupe.
L'en-tête sert à définir la disposition de votre structure afin d'aider le reste du logiciel à utiliser l'implémentation. Vous voudrez peut-être voir cela comme une "abstraction" de votre mise en œuvre, ce qui est dit vaughly, mais, je pense que cela convient assez bien dans ce cas.
Comme les affiches précédentes l'ont dit et montré, vous déclarez les zones d'utilisation privée et publique et leurs en-têtes, cela inclut également les variables privées et publiques. Maintenant, je ne veux pas entrer dans la conception du code ici, mais vous voudrez peut-être examiner ce que vous mettez dans vos en-têtes, puisqu'il s'agit de la couche entre l'utilisateur final et l'implémentation.