Dans tous nos cours c ++, tous les enseignants mettent toujours using namespace std;
Juste après le #include
Dans leurs fichiers .h
. Cela me semble dangereux car, en incluant cet en-tête dans un autre programme, je vais importer l’espace de noms dans mon programme, peut-être sans le réaliser, le vouloir ou le vouloir (l’inclusion d’en-têtes peut être très profondément imbriquée).
Ma question est donc double: ai-je raison de dire que using namespace
Ne devrait pas être utilisé dans les fichiers d'en-tête, et/ou existe-t-il un moyen de l'annuler, quelque chose comme:
//header.h
using namespace std {
.
.
.
}
Encore une question dans le même sens: un fichier d’en-tête #include
Doit-il contenir tous les en-têtes qu’il correspond au fichier .cpp
, Uniquement ceux nécessaires aux définitions d’en-tête et laisser le .cpp
fichier #include
le reste, ou aucun et déclare tout ce dont il a besoin comme extern
?
Le raisonnement derrière la question est le même que ci-dessus: je ne veux pas de surprises en incluant des fichiers .h
.
Aussi, si j'ai raison, est-ce une erreur commune? Je veux dire dans la programmation du monde réel et dans les "vrais" projets là-bas.
Merci.
Vous ne devez absolument PAS utiliser using namespace
dans les en-têtes précisément pour la raison que vous dites, cela peut modifier de manière inattendue la signification du code dans tout autre fichier contenant cet en-tête. Il n'y a aucun moyen de défaire un using namespace
ce qui est une autre raison pour laquelle il est si dangereux. J'utilise généralement simplement grep
ou similaire pour m'assurer que using namespace
n'est pas appelé dans les en-têtes plutôt que d'essayer quelque chose de plus compliqué. Les vérificateurs de code statiques le signalent probablement aussi.
L'en-tête doit inclure uniquement les en-têtes qu'il a besoin de compiler. Un moyen simple de le faire est d'inclure toujours l'en-tête de chaque fichier source comme première chose, avant tout autre en-tête. Ensuite, la compilation du fichier source échouera si l'en-tête n'est pas autonome. Dans certains cas, par exemple en faisant référence aux classes de détail d'implémentation au sein d'une bibliothèque, vous pouvez utiliser des déclarations en aval au lieu de #include
parce que vous avez le contrôle total sur la définition de cette classe déclarée à terme.
Je ne suis pas sûr que j'appellerais cela du commun, mais cela apparaît clairement de temps en temps, généralement écrit par de nouveaux programmeurs qui ne sont pas conscients des conséquences négatives. Généralement, un peu d’information sur les risques résout tous les problèmes car il est relativement simple à résoudre.
Point 59 de Sutter et Alexandrescu "Normes de codage C++: 101 règles, directives et meilleures pratiques" :
59. N'écrivez pas les utilisations d'espaces de noms dans un fichier d'en-tête ou avant un #include.
Les espaces de noms
using
s sont destinés à votre commodité et ne doivent pas être infligés à d'autres: n'écrivez jamais une déclarationusing
ou une directiveusing
avant une directive#include
.Corollaire: dans les fichiers d'en-tête, n'écrivez pas les directives
using
ouusing
au niveau de l'espace de noms; au lieu de cela, explicitement l'espace de noms qualifie tous les noms.
Un fichier d'en-tête est un invité dans un ou plusieurs fichiers source. Un fichier d'en-tête qui inclut les directives et les déclarations using
apporte également ses copains tapageurs.
Une using
déclaration amène un ami. Une using
directive fait entrer tous les amis dans l'espace de noms. L'utilisation de using namespace std;
Par vos enseignants est une directive d'utilisation.
Plus sérieusement, nous avons des espaces de noms pour éviter les conflits de noms. Un fichier d'en-tête est destiné à fournir une interface. La plupart des en-têtes ne connaissent pas le code qui peut les inclure, maintenant ou à l'avenir. L'ajout d'instructions using
à des fins pratiques à l'intérieur de l'en-tête confère ces noms commodes à tous les clients potentiels de cet en-tête. Cela peut conduire à un conflit de noms. Et c'est tout simplement grossier.
Vous devez faire attention lorsque vous incluez des en-têtes à l'intérieur des en-têtes. Dans les grands projets, cela peut créer une chaîne de dépendance très complexe qui déclenche des reconstructions plus grandes/plus longues que nécessaire. Consultez cet article et son suivi pour en savoir plus sur l'importance d'une bonne structure physique dans les projets C++.
Vous ne devez inclure des en-têtes à l'intérieur d'un en-tête qu'en cas d'absolue nécessité (chaque fois que la définition complète d'une classe est requise), et utilisez la déclaration forward chaque fois que vous le pouvez (lorsque la classe est requise, il s'agit d'un pointeur ou d'une référence).
En ce qui concerne les espaces de noms, j’ai tendance à utiliser la portée de l’espace de noms explicite dans mes fichiers d’en-tête et à ne mettre qu'un using namespace
dans mes fichiers cpp.
Consultez les normes de codage du Goddard Space Flight Center (pour C et C++). Cela s'avère être un peu plus difficile qu'auparavant - voir les réponses mises à jour aux SO questions:
La norme de codage GSFC C++ dit:
§3.3.7 Chaque fichier d'en-tête doit
#include
les fichiers qu’il doit compiler, plutôt que de forcer les utilisateurs à#include
les fichiers nécessaires.#includes
doit être limité à ce dont l'en-tête a besoin; autre#includes
devrait être placé dans le fichier source.
La première des questions à référence croisée inclut désormais une citation de la norme de codage GSFC C, ainsi que la justification, mais la substance finit par être la même.
Vous avez raison que using namespace
dans l'en-tête est dangereux. Je ne sais pas comment le défaire. Il est facile de le détecter, mais il suffit de rechercher using namespace
dans les fichiers d'en-tête. Pour cette dernière raison, il est rare dans les projets réels. Les collègues plus expérimentés vont bientôt se plaindre si quelqu'un fait quelque chose de semblable.
Dans les projets réels, les utilisateurs essaient de minimiser le nombre de fichiers inclus, car moins vous en incluez, plus la compilation est rapide. Cela fait gagner du temps à tout le monde. Toutefois, si le fichier d'en-tête suppose que quelque chose doit être inclus avant, il doit l'inclure lui-même. Sinon, les en-têtes ne sont pas autonomes.
En ce qui concerne "Y a-t-il un moyen d'annuler [une using
déclaration]?"
Je pense qu’il est utile de souligner que les déclarations using
sont affectées par la portée.
#include <vector>
{ // begin a new scope with {
using namespace std;
vector myVector; // std::vector is used
} // end the scope with }
vector myOtherVector; // error vector undefined
std::vector mySTDVector // no error std::vector is fully qualified
Donc effectivement oui. En limitant la portée de la déclaration using
, son effet ne dure que dans cette portée; il est "défait" lorsque cette portée se termine.
Lorsque la déclaration using
est déclarée dans un fichier situé en dehors de toute autre étendue, elle a une portée de fichier et affecte tout ce qui est contenu dans ce fichier.
Dans le cas d'un fichier d'en-tête, si la déclaration using
est dans file-scope, cela étendra la portée de tout fichier dans lequel l'en-tête est inclus.
Comme toutes les choses dans la programmation, le pragmatisme devrait l'emporter sur le dogmatisme, IMO.
Tant que vous prenez la décision à l'échelle du projet ("Notre projet utilise intensivement STL et que nous ne voulons pas tout prévoir avec std ::."), Je ne vois pas le problème avec cela. La seule chose que vous risquez, ce sont les collisions de noms, après tout, et avec l'omniprésence de STL, il ne devrait pas y avoir de problème.
D'autre part, si c'était la décision d'un développeur dans un seul fichier d'en-tête (non privé), je peux voir en quoi cela générerait de la confusion parmi l'équipe et devrait être évité.
Vous avez raison. Et tout fichier ne doit inclure que les en-têtes nécessaires à ce fichier. En ce qui concerne "est-ce que les mauvaises choses sont communes dans les projets du monde réel?" - Oh oui!
Je pense que vous pouvez utiliser "utiliser" dans les en-têtes C++ en toute sécurité si vous écrivez vos déclarations dans un espace de noms imbriqué comme celui-ci:
namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED
{
/*using statements*/
namespace DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED
{
/*declarations*/
}
}
using namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED::DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED;
Cela ne devrait inclure que les choses déclarées dans 'DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED' sans les espaces de noms utilisés. Je l'ai testé sur le compilateur mingw64.