web-dev-qa-db-fra.com

"using namespace" dans les en-têtes c ++

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.

110
Baruch

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.

105
Mark B

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 usings sont destinés à votre commodité et ne doivent pas être infligés à d'autres: n'écrivez jamais une déclaration using ou une directive using avant une directive #include.

Corollaire: dans les fichiers d'en-tête, n'écrivez pas les directives using ou using 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.

23
Andy Thomas

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.

12
Mike O'Connor

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.

6

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.

5
Öö Tiib

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.

4
YoungJohn

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

4
ijprest

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!

4

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.

2
AnArrayOfFunctions