Organisation des fichiers d’en-tête de classe C ++

Quelles sont les directives de codage et d’organisation de fichiers C ++ que vous proposez aux personnes devant gérer un grand nombre de classes interdépendantes réparties sur plusieurs fichiers source et en-tête?

J’ai cette situation dans mon projet et la résolution des erreurs liées à la définition de classe qui traversent plusieurs fichiers d’en-tête est devenue un casse-tête.

Quelques lignes direcsortingces générales:

  • Associez vos interfaces aux implémentations. Si vous avez foo.cxx , tout ce qui y est défini devrait être déclaré dans foo.h
  • Assurez-vous que chaque fichier d’en-tête # inclut tous les autres en-têtes ou déclarations préalables nécessaires à la compilation indépendante.
  • Résistez à la tentation de créer un en-tête “tout”. Ils ont toujours des problèmes sur la route.
  • Placez un ensemble de fonctionnalités liées (et interdépendantes) dans un seul fichier. Java et d’autres environnements encouragent une classe par fichier. Avec C ++, vous voulez souvent un ensemble de classes par fichier. Cela dépend de la structure de votre code.
  • Préférer la déclaration sur #include s autant que possible. Cela vous permet de casser les dépendances cycliques de l’en-tête. Essentiellement, pour les dépendances cycliques entre des fichiers distincts, vous voulez un graphique de dépendance de fichier qui ressemble à ceci:
    • A.cxx nécessite Ah et Bh
    • B.cxx nécessite Ah et Bh
    • Ah nécessite Bh
    • Bh est indépendant (et les classes forward-declares définies dans Ah )

Si votre code est destiné à être une bibliothèque consommée par d’autres développeurs, il est important de prendre certaines mesures supplémentaires:

  • Si nécessaire, utilisez le concept “en-têtes privés”. C’est-à-dire les fichiers d’en-tête requirejs par plusieurs fichiers sources, mais jamais requirejs par l’interface publique. Cela pourrait être un fichier avec des fonctions en ligne, des macros ou des constantes internes communes.
  • Séparez votre interface publique de votre implémentation privée au niveau du système de fichiers. J’ai tendance à utiliser include/ sous src/ répertoires include/ et src/ dans mes projets C ou C ++, où include/ a tous mes en-têtes publics et src/ a toutes mes sources. et en-têtes privés.

Je vous recommande de trouver une copie du livre de John Lakos intitulé Large-Scale C ++ Software Design . C’est un livre assez lourd, mais si vous parcourez quelques-unes de ses discussions sur l’architecture physique, vous en apprendrez beaucoup.

Consultez les normes de codage C et C ++ du Goddard Space Flight Center de la NASA . La seule règle que j’ai notée dans la norme C et adoptée dans mon propre code est celle qui impose la nature «autonome» des fichiers d’en-tête. Dans le fichier d’implémentation xxx.cpp pour l’en-tête xxx.h, assurez-vous que xxx.h est le premier en-tête inclus. Si l’en-tête n’est pas autonome à tout moment, la compilation échouera. C’est une règle magnifiquement simple et efficace.

La seule fois où vous échouez, c’est si vous portez entre les ordinateurs, et que l’en-tête xxx.h inclut, par exemple, , mais que requirejs des fonctionnalités mises à disposition par un en-tête sur la plateforme d’origine (donc inclut ), mais les fonctionnalités ne sont pas mises à disposition par sur l’autre plate-forme (elles sont à la place dans def.h , mais n’inclut pas ). Ce n’est pas une faute de la règle, et le problème est plus facilement diagnostiqué et corrigé si vous suivez la règle.

Vérifiez la section du fichier d’en-tête dans le guide de style Google

La réponse de Tom est excellente!

La seule chose que j’appendais est de ne jamais avoir de “utilisation des déclarations” dans les fichiers d’en-tête. Ils ne devraient être autorisés que dans les fichiers d’implémentation, par exemple foo.cpp .

La logique pour cela est bien décrite dans l’excellent livre “Accelerated C ++” ( lien Amazon – désinfecté pour script kiddie link nazis)

Un autre point en plus des autres ici:

N’incluez aucune définition privée dans un fichier d’inclusion. Par exemple, toute définition utilisée uniquement dans xxx.cpp doit être dans xxx.cpp et non xxx.h.

Cela semble évident, mais je le vois fréquemment.

Je voudrais append une très bonne pratique (à la fois en C et C ++) qui est souvent abandonnée:

foo.c

 #include "foo.h" // always the first directive 

Les autres en-têtes nécessaires doivent suivre, puis coder. Le fait est que vous avez presque toujours besoin de cet en-tête pour cette unité de compilation et l’inclure en tant que première directive garantit que l’en-tête rest auto-suffisant (si ce n’est pas le cas, il y aura des erreurs). Cela est particulièrement vrai pour les en-têtes publics.

Si à tout moment vous devez mettre quelque chose avant l’inclusion de cet en-tête (à l’exception des commentaires, bien sûr), il est probable que vous faites quelque chose de mal. Sauf si vous savez vraiment ce que vous faites … ce qui conduit à une autre règle plus cruciale => commentez vos hacks!