web-dev-qa-db-fra.com

Que devrait-on mettre dans un fichier .h?

Lorsque vous divisez votre code en plusieurs fichiers, que faut-il exactement dans un fichier .h et dans un fichier .cpp?

88
Enrico Tuvera Jr

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.

103
Amber

En réalité, en C++, ceci est un peu plus compliqué que l’organisation en-tête/source C.

Que voit le compilateur?

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.

Alors, pourquoi les en-têtes sont-ils nécessaires?

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

Rien de plus?

En C++, d'autres éléments pourraient être placés dans l'en-tête, car ils doivent également être partagés:

  • code en ligne
  • des modèles
  • constantes (généralement celles que vous souhaitez utiliser à l'intérieur des commutateurs ...)

Déplacer vers l'en-tête TOUT ce qui doit être partagé, y compris les implémentations partagées

Cela signifie-t-il qu'il peut y avoir des sources dans les en-têtes?

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).

  • Déclarations en avant
  • déclarations/définition de fonctions/structures/classes/modèles
  • implémentation de code en ligne et basé sur un modèle

Cela devient compliqué, et dans certains cas (dépendances circulaires entre les symboles), impossible de le conserver dans un en-tête.

Les en-têtes peuvent être divisés en trois parties

Cela signifie que, dans un cas extrême, vous pourriez avoir:

  • un en-tête de déclaration
  • un en-tête de déclaration/définition
  • un en-tête d'implémentation
  • une source d'implémentation

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.

Hou la la!

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é.

Conclusion

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 ...

^ _ ^

50
paercebal

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.

16
Adrien Plisson

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.

6
Pavel Radzivilovsky

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.

3
anon

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).

1
Alexander Gessler

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).

1
Ashish

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();
}
0
jose

Je m'attendrais à voir:

  • déclarations
  • commentaires
  • définitions marquées en ligne
  • des modèles

la vraie réponse est cependant de ne pas mettre dans:

  • définitions (peuvent conduire à la définition multiple des choses)
  • utiliser des déclarations/directives (les oblige à tout le monde, y compris votre en-tête, peut provoquer des conflits de noms)
0
jk.

En-tête (.h)

  • Macros et inclus nécessaires pour les interfaces (aussi peu que possible)
  • La déclaration des fonctions et des classes
  • Documentation de l'interface
  • Déclaration de fonctions/méthodes inline, le cas échéant
  • extern to global variables (le cas échéant)

Corps (.cpp)

  • Reste des macros et comprend
  • Inclure l'en-tête du module
  • Définition des fonctions et des méthodes
  • Variables globales (le cas échéant)

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

0
Khelben

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.

  • Vous définissez la "disposition" de votre structure, en ne disant que ce qui est nécessaire aux groupes d’utilisation environnants.
  • Les définitions d'une variable, d'une fonction et d'une classe.

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.

0
Filip Ekberg
  • Fichiers d'en-tête - ne devraient pas changer trop souvent au cours du développement -> vous devriez réfléchir et les écrire immédiatement (dans le cas idéal)
  • Fichiers source - modifications en cours de mise en œuvre
0
nothrow