12 votes

Les déclarations '#include' et 'using' doivent-elles être répétées dans les fichiers d'en-tête et d'implémentation (C++) ?

Je suis assez nouveau en C++, mais ce que je comprends, c'est qu'une déclaration #include va essentiellement simplement déverser le contenu du fichier #inclus à l'emplacement de cette déclaration. Cela signifie que si j'ai un certain nombre de déclarations '#include' et 'using' dans mon fichier d'en-tête, mon fichier d'implémentation peut simplement inclure le fichier d'en-tête, et le compilateur ne se souciera pas si je ne répète pas les autres déclarations.

Et les personnes alors?

Ma principale préoccupation est que si je ne répète pas les déclarations '#include', 'using', et aussi 'typedef' (maintenant que j'y pense), cela retire ces informations du fichier dans lequel ils sont utilisés, ce qui pourrait causer de la confusion.

Je travaille actuellement sur de petits projets où cela ne posera pas vraiment de problèmes, mais je peux imaginer que dans de plus grands projets avec plus de personnes qui y travaillent, cela pourrait devenir un problème significatif.

Un exemple suit:

MISE À JOUR: les prototypes de mes fonctions pour 'Unit' ont des types de retour et des paramètres comme string, ostream et StringSet - je n'inclus rien dans mon fichier d'en-tête qui est utilisé uniquement dans le fichier d'implémentation.

//Unit.h

#include 
#include 
#include "StringSet.h"

using std::string;
using std::ostream;

class Unit {

public:
    //membres publics avec string, ostream et StringSet
    //dans leurs valeurs de retour/listes de paramètres
private:
    //membres privés
    //question annexe : les membres privés
    //devraient-ils même être inclus dans le fichier d'en-tête?
} ;

//Unit.cpp

#include "Unit.h"

//Ce qui suit est tout redondant d'un point de vue du compilateur:
#include 
#include 
#include "StringSet.h"

using std::string;
using std::ostream;

//implémentation va ici

11voto

Travis Gockel Points 11043

Une directive d'utilisation (using namespace std;) ne devrait pas se trouver dans un en-tête à moins d'être contenue dans une fonction. C'est une mauvaise pratique. Il est peu probable que chaque utilisateur de votre en-tête souhaite une recherche non qualifiée pour tout dans un espace de noms donné. L'inclusion d'en-têtes non liés peut entraîner des ambiguïtés inattendues et des échecs de compilation. Personnellement, j'évite la directive d'utilisation à l'intérieur des fonctions pour la même raison, mais cela est généralement considéré comme moins nocif.

Un alias de type (soit à travers typedef std::string string; ou using string = std::string;) doit être utilisé avec précaution. Les définitions de types ont un sens, donc vous ne devriez jamais le redéclarer. Par exemple, c'est une erreur:

typedef int   myint;
typedef float myint;

à cause de conflits de types.

Une déclaration d'utilisation (using std::string; ou using std::memcpy;) rend un symbole accessible pour la recherche de nom non qualifiée. C'est extrêmement utile pour obtenir une recherche dépendante des arguments correcte, ce qui n'a généralement pas d'importance à moins que vous n'écriviez une bibliothèque. Le conseil est différent selon que vous importez un type ou une fonction. Pensez aux déclarations d'utilisation avec des types de la même manière qu'à un alias de type : il n'a pas de sens d'avoir plusieurs définitions sous le même nom. Avec les fonctions, tout ce que vous faites réellement, c'est d'étendre la résolution de surcharge pour inclure quelques éléments supplémentaires (bien que ce ne soit généralement pas nécessaire).

// Trouver plusieurs fonctions operator<< a du sens
using std::operator<<;
using mylib::operator<<;

// Trouver plusieurs classes string n'a pas de sens
using std::string;
using mylib::string;

Pour les #include répétées, vous devriez considérer si vous avez réellement besoin d'inclure le fichier dans l'en-tête en premier lieu. Peut-être qu'une déclaration anticipée correspond à vos besoins.

7voto

paercebal Points 38526
  • Incluez uniquement dans un en-tête/source ce dont vous avez vraiment besoin (si des déclarations anticipées sont disponibles et suffisantes, déclarez-les plutôt que de les inclure)
  • N'utilisez pas l'instruction using dans les en-têtes (sauf à l'intérieur des portées de fonction)... Ajouter using dans l'en-tête polluera les espaces de noms de toutes les sources incluant l'en-tête.
  • Assurez-vous que chaque fichier (en-tête ou source) inclut tout ce dont il a besoin, et rien de plus.

Vous n'avez pas besoin de vous soucier si certains inclusions sont redondantes. Les protections d'en-tête et les optimisations du préprocesseur sont là pour gérer cela pour vous.

Vous devriez être capable de manipuler chaque fichier de façon isolée.

Par exemple, disons que vous utilisez le std::string dans l'en-tête et dans le source, mais, comme "optimisation", vous avez seulement inclus le string dans l'en-tête... Si vous découvrez plus tard que vous n'avez plus besoin du string dans l'en-tête, et que vous souhaitez le supprimer (pour nettoyer le code, etc.), vous devrez modifier le source pour inclure le string. Maintenant, imaginez que vous avez DIX sources incluant l'en-tête...

Évidemment, il peut y avoir des exceptions à cette règle (par exemple, les en-têtes précompilés, ou même les en-têtes dont le seul but est d'inclure plusieurs fichiers en guise de courtoisie), mais par défaut, vous devriez avoir des fichiers en-tête et source autosuffisants (c'est-à-dire des fichiers qui incluent tout ce qu'ils utilisent, ni plus ni moins).

0voto

Nick Points 76

Gardez les fichiers d'en-tête au minimum. Cela signifie aussi peu d'inclusions que possibles. Le fichier .cpp inclura généralement l'en-tête correspondant ainsi que tout autre en-tête nécessaire pour l'implémentation.

0voto

Alex Korban Points 7819

Comme Travis l'a dit, vous ne devriez pas avoir des déclarations using dans un fichier d'en-tête parce que cela signifie qu'elles seront incluses dans toutes les unités de traduction qui incluent ce fichier d'en-tête, ce qui peut causer des problèmes de confusion.

Si j'ai seulement besoin de la fonctionnalité d'un fichier d'en-tête dans un fichier cpp, je le inclut seulement dans ce fichier cpp. C'est une bonne pratique pour les projets plus importants car cela signifie moins de travail pour le compilateur. De plus, autant que possible, j'utilise des déclarations antérieures dans les en-têtes au lieu d'inclure des fichiers (et encore une fois, inclure les en-têtes dans le fichier cpp).

0voto

Alan Points 3489

Il est considéré comme une mauvaise pratique d'avoir une instruction using dans un fichier d'en-tête, sauf si vous dupliquez intentionnellement un symbole dans un espace de noms différent. Il est acceptable de l'utiliser dans un fichier cpp.

Chaque typedef ne doit exister qu'une seule fois dans votre code. Cela devrait être dans un fichier d'en-tête s'il doit être utilisé dans plusieurs fichiers cpp/h. Les dupliquer vous causera beaucoup de problèmes.

Un fichier d'en-tête devrait contenir toutes les instructions #include dont il a besoin, et pas d'autres. Si seuls des pointeurs vers une classe sont mentionnés, utilisez une déclaration anticipée plutôt que d'inclure l'en-tête. Tous les autres inclusions nécessaires uniquement à l'intérieur du fichier cpp doivent y être placées. Répéter les inclusions de l'en-tête est acceptable, mais pas obligatoire. C'est juste un choix de style.

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