52 votes

Votre politique d'en-tête C / C ++ préférée pour les grands projets?

Lorsque vous travaillez sur un gros C/C++ du projet, avez-vous quelques règles spécifiques concernant le #inclure à l'intérieur de la source ou de fichiers d'en-tête?

Par exemple, nous pouvons imaginer de suivre l'une de ces deux excessive des règles:

  1. #include sont interdits .h des fichiers; il revient à chaque .c fichier à inclure tous les en-têtes il a besoin
  2. Chaque .h fichier doit inclure l'ensemble de ses dépendances, c'est à dire qu'il doit être capable de compiler toute seule sans erreur.

Je suppose qu'il est un compromis entre les deux pour n'importe quel projet, mais quelle est la vôtre? Avez-vous des règles plus spécifiques? Ou sur un lien qui plaide en faveur d'une des solutions?

42voto

Mecki Points 35351

En train de faire .h comprend uniquement des fichiers dans C à dire que si je viens d'inclure un en-tête (définition de quelque chose, que je veux utiliser le fichier C) peut échouer. Elle peut échouer parce que j'ai l'inclusion de 20 autres en-têtes à l'avance. Et même pire, j'ai l'inclure dans le bon ordre. Avec une vraie beaucoup de .h fichiers, ce système finit par être une administration de l'enfer dans le long terme. Vous voulez juste de comprendre un .h fichier dans un .c fichier et que vous passez 2 heures pour découvrir les autres .h les fichiers dont vous avez besoin et dans quel ordre vous devez les inclure.

Si un .h fichier besoin de l'autre .h fichier inclus avec succès dans un fichier C qui ne comprennent pas autre chose que cela .h fichier et sans causer des erreurs de compilation, je vais inclure cette autres .h fichier dans le .h fichier lui-même. De cette façon, je sais pour sûr que chaque C fichier peut inclure tous les .h fichier et il ne sera jamais provoquer des erreurs. L' .c fichier n'a jamais à se soucier des autres .h fichiers pour l'importation ou l'ordre dans lequel les inclure. Cela fonctionne même avec d'énormes projets (1000 .h des fichiers de et vers le haut).

D'autre part, je n'ai jamais inclure un .h en un autre, si ce n'est pas nécessaire. E. g. si j'ai de la table de hachage.h et de table de hachage.c, et de la table de hachage.c besoins de hachage.h, mais à la table de hachage.h n'en a pas besoin, je ne comprend pas là. Je ne l'inclure dans le .c fichier, comme les autres .c fichiers, y compris la table de hachage.h n'avez pas besoin de hachage.h et si ils en ont besoin pour quelque raison que ce soit, ils doivent comprendre.

17voto

PierreBdR Points 11479

Je pense que tous deux suggèrent règles sont mauvais. Dans ma partie, j’ai toujours appliquer :

Inclure uniquement les fichiers d’en-têtes nécessaires pour compiler un fichier en utilisant uniquement ce qui est défini dans cet en-tête. Cela signifie :

  1. Tous les objets présent comme référence ou pointeurs seulement doivent être déclarée avant
  2. Inclure tous les en-têtes en définissant des fonctions ou des objets utilisés dans l’en-tête lui-même.

13voto

paercebal Points 38526

Je voudrais utiliser la règle 2:

Tous les en-Têtes doivent être auto-suffisante, que ce soit par:

  • ne pas utiliser quoi que ce soit définie ailleurs
  • avant de déclarer les symboles définis ailleurs
  • y compris les en-têtes de définir les symboles qui ne peuvent pas être l'avant-déclaré.

Donc, si vous avez un vide C/C++ source file, y compris un en-tête doit compiler correctement.

Ensuite, dans le C/C++ source file inclure que ce qui est nécessaire: Si HeaderA de l'avant-déclaré un symbole défini dans HeaderB, et que vous utilisez ce symbole, vous devez inclure à la fois... La bonne nouvelle étant que si vous n'utilisez pas l'avant-déclaré symbole, alors vous serez en mesure d'inclure seulement HeaderA, et d'éviter d'inclure HeaderB.

Notez que de jouer avec des modèles rend cette vérification "vider la source, y compris votre tête doit compiler" un peu plus compliqué (et amusant...)

8voto

Konrad Rudolph Points 231505

La première règle échoue dès qu'il y a des dépendances circulaires. Il ne peut donc pas être appliquée strictement.

(Cela peut encore être fait pour travailler, mais celle-ci se déplace d'un ensemble de beaucoup de travail de la programmeur pour le consommateur de ces bibliothèques qui est évidemment faux.)

Je suis tout à fait en faveur de la règle 2 (bien qu'il pourrait être bon d'inclure "déclaration anticipée en-têtes" à la place de la vraie affaire, comme en <iosfwd> , car cela réduit le temps de compilation). Généralement, je crois que c'est une sorte d'auto-documentation si un fichier d'en-tête "déclare" quelles sont les dépendances qu'il a – et quelle meilleure façon de le faire est d'inclure les fichiers nécessaires?

EDIT:

Dans les commentaires, j'ai été contesté que les dépendances circulaires entre les en-têtes sont un signe de mauvaise conception et doit être évitée.

Ce n'est pas correct. En fait, les dépendances circulaires entre les classes peuvent être inévitables et ne sont pas un signe de mauvaise conception. Les exemples sont abondants, permettez-moi de mentionner l'Observateur modèle qui a une référence circulaire entre l'observateur et l'objet.

Pour résoudre la circularité entre les classes, vous devez employer l'avant de la déclaration parce que l'ordre de déclaration des questions en C++. Maintenant, il est tout à fait acceptable pour gérer cette déclaration dans une circulaire de manière à réduire le nombre total de fichiers et de centraliser le code. Certes, dans cette affaire, qui n'a pas le mérite de ce scénario, car il n'y a qu'une seule déclaration anticipée. Cependant, j'ai travaillé sur une bibliothèque où il y a beaucoup plus.

// observer.hpp

class Observer; // Forward declaration.

#ifndef MYLIB_OBSERVER_HPP
#define MYLIB_OBSERVER_HPP

#include "subject.hpp"

struct Observer {
    virtual ~Observer() = 0;
    virtual void Update(Subject* subject) = 0;
};

#endif


// subject.hpp
#include <list>

struct Subject; // Forward declaration.

#ifndef MYLIB_SUBJECT_HPP
#define MYLIB_SUBJECT_HPP

#include "observer.hpp"

struct Subject {
    virtual ~Subject() = 0;
    void Attach(Observer* observer);
    void Detach(Observer* observer);
    void Notify();

private:
    std::list<Observer*> m_Observers;
};

#endif

4voto

David Sykes Points 9683

Une version minimale de 2. fichiers .h incluent uniquement les fichiers d’en-tête que demande expressément à compiler, en utilisant la déclaration anticipée et pimpl autant qu’il est pratique.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X