web-dev-qa-db-fra.com

using namespace std; dans un fichier d'en-tête

Donc, j'ai les éléments suivants dans un fichier de spécification

#include <string>
#include <fstream>
using namespace std:

class MyStuff
{
    private:

    string name;
    fstream file;
    // other stuff

    public:
    void setName(string);
}

J'ai aussi dans le fichier d'implémentation

#include "MyStuff.h"
using namespace std;

void MyStuff::setName(string name);
{
     name = name
}

et dans le fichier programme j'ai ...

#include <iostream>
#include <string>
using namespace std;

void main()
{
     string name;
     MyStuff Stuff;

     cout << "Enter Your Name: ";
     getline(cin, name);

     Stuff.setName(name);
}

Et je rassemble cette application "using namespace std;" dans un fichier d’en-tête, c’est un non-non, et qualifier pleinement est la "meilleure" pratique; comme std::cout << stuff << endl;

Je crois comprendre que pour utiliser une chaîne, elle doit avoir l’espace de nom std. Est-ce vrai?

Si c'est le cas, dans le fichier d'en-tête, il est plus "pur/propre" de le faire en tant que ...

#include <string>

class MyStuff
{
     std::string name;
}

Et, si je comprends bien, en utilisant namespace std; Les trois fichiers, spécification, implémentation et programme, superposent essentiellement les trois espaces de noms. Ainsi, si je déclare séparément string name; dans chacun des fichiers, le compilateur ne saura pas qui va à quoi. Est-ce vrai?

Je comprends généralement qu'être clair est une "bonne" chose à faire. Cependant, je ne suis pas assez clair sur la spécificité de savoir comment, et je suis plus intéressé par le "pourquoi" plus profond qui sous-tend tout cela.

Ma question directe est donc, dans mon exemple fourni, quelle est la manière "la plus claire" de décrire la fonction à la fois pour le compilateur et pour le "standard" de l'industrie? Et, pouvez-vous me diriger vers des ressources qui définissent plus clairement le raisonnement et la mise en œuvre pratique des espaces de noms.

13
RebelPhoenix

Disons que je déclare une classe string moi-même. Parce que je suis un fainéant, je le fais dans un espace de noms global.

// Solar's stuff
class string
{
    public:
        string();
        // ...
};

Quelque temps plus tard, je réalise que réutiliser certains de votre code profiterait à mon projet. Grâce à vous le rendant Open Source, je peux le faire:

#include <solarstuff.hpp>
#include <phoenixstuff.hpp>

string foo;

Mais tout à coup le compilateur ne m'aime plus. Puisqu'il existe un ::string (ma classe) et une autre ::string (le standard, inclus dans votre en-tête et introduit dans l'espace de noms global avec using namespace std;), il y a toutes sortes de difficultés à surmonter.

Pire encore, ce problème est mis en avant dans tous les fichiers contenant my en-tête (qui inclut votre en-tête, qui ... vous avez l’idée.)

Oui, je sais, dans cet exemple, je suis également responsable de ne pas protéger mes propres classes dans mon propre espace de noms, mais c'est celui que j'ai proposé ad-hoc.

Les espaces de noms sont là pour éviter les conflits d'identifiants. Votre en-tête introduit non seulement MyStuff dans l’espace de nom global, mais également every identifier à partir de string et fstream. Il est fort probable que ni l'un ni l'autre d'entre nous n'a besoin de la plupart d'entre eux. Pourquoi les traîner dans le monde, polluer l'environnement?

Addition: Du point de vue d'un codeur/débogueur de maintenance, foo::MyStuff est dix fois plus pratique que MyStuff, un espace de noms ailleurs (probablement même pas le même fichier source), car vous obtenez les informations sur les espaces de noms directement à pointez dans le code où vous en avez besoin.

10
DevSolar

Plusieurs instances de using namespace std; ne créeront aucune ambiguïté. Le problème est que cette instruction importe tous les noms/types/fonctions de std dans votre espace de noms, et maintenant, si vous souhaitez nommer une classe string par exemple, vous aurez des problèmes. Cela est plus susceptible de se produire avec des fonctions telles que supprimer, effacer, etc. 

L'utilisation de dans les en-têtes est un niveau pire car cela se propage à tous les .cpps de cet en-tête, sans que la personne l'ignore. Son utilisation dans .cpp nécessite au moins un choix conscient.

A explication plus complète peut être obtenu à Pourquoi "using namespace std" est-il considéré comme une mauvaise pratique?

Un exemple de problèmes pouvant en résulter peut être trouvé dans Comment utiliser un itérateur? où l'OP définit une fonction distance et continue à obtenir la mauvaise réponse. Autre exemple at Confusion sur les pointeurs et les références en C++

8
Karthik T

Les utilisations des espaces de noms sont faites pour votre commodité et non pour infliger à 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 au niveau de l'espace de noms à l'aide de directives ou de déclarations; au lieu de cela, explicitement les espaces de noms qualifient tous les noms. (La deuxième règle découle de la première, car les en-têtes ne peuvent jamais savoir quel autre en-tête #includes pourrait apparaître après eux.)

Je comprends généralement qu'être clair est une "bonne" chose à faire. Je suis Mais je ne suis pas assez clair sur la spécificité de la façon dont, et je suis plus Intéressé par le "pourquoi" plus profond qui sous-tend tout.

Les extraits de code ci-dessous montrent pourquoi l'utilisation de using namespace dans le fichier d'en-tête est incorrecte:

// snippet 1
namespace A {
 int f(double);
}

// snippet 2
namespace B {  
    using A::f;
    void g();
}

// snippet 3
namespace A {
    int f(int);
}

// snippet 4
void B::g() {
    f(1);   // which overload is called?
}

Donc dans votre exemple, celui-ci est simplement meilleur:

#include <string>

class MyStuff
{
    std::string name;
};

Recommander ce livre: Normes de codage C++: 101 règles, directives et meilleures pratiques

et lien: Guide de codage Google C++

2
billz