
Envoyé par
Legro
Excusez moi,mais je ne saisis pas bien le problème de l'utilisation de using namespace std;pouriez vous m'aider à le comprendre ? Qu'est ce que "briser" le système d'espace de noms ? Quel est le risque ou bien l'inconvénient ?
Merci
Bon, cette réponse va encore me faire écrire un véritable roman... Accroche toi 
Car, pour comprendre le problème, il faut déjà comprendre ce qu'est un espace de noms. Nous allons donc commencer par là 
Nous pourrions comparer les espaces de noms à des boites de rangements, dans lesquelles tu pourrais placer les choses "qui vont bien ensembles".
Tu pourrais donc avoir une boite estampillée (un espace de noms nommé) "outils" dans laquelle tu rangerais tous tes outils et un(e) autre nommé "cuisine" dans laquelle tu rangerais toutes tes affaires de cuisines.
La seule chose, c'est que tu peux aussi décider de mettre une ou plusieurs "boites plus petites" à coté des éléments d'ordre "plus généraux". Par exemple, tu pourrais avoir ton marteau et tes tournevis dans la boite "outils", mais avoir une boite plus petite qui contiendrait les outils destinés exclusivement au jardinage, une autre qui contiendrait les outils destinés exclusivement à l'électricité, et une troisième qui contiendrait les outils destinés exclusivement la mécanique.
C'est très cool, car tu sais que, si tu as besoin d'un outil, tu le trouvera dans la boite "outils" et non dans la boite "cuisine", et que, de même, si tu as besoin d'un râteau, tu le trouveras dans la boite "jardin" qui se trouve dans la boite "outils" et non dans la boite "mécanique" qui se trouve aussi dans la boite "outils".
Quand je dis que la directive using namespace XXX; "brise le système d'espace de noms", c'est parce que cela revient strictement au même que de ... renverser l'ensemble de la boite dans laquelle tu as "si bien tout rangé" sur une table.
Quant tu ne le fais qu'avec la boite "outils", ce "n'est pas si grave", parce que les boîtes "jardin","électricité" et "mécanique" sont "bien fermées (et ne risquent donc pas de se vider par la même occasion), mais essaye d'imaginer le résultat si tu décidais de renverser -- sur la même table -- le contenu de la boite "cuisine"... :
tu retrouverais ton marteau et tes tournevis entourés de louches, de couteaux à découper et de lèches-plats... Outre le fait que ce ne serait pas très hygiénique d'utiliser un couteau qui vient de se frotter au manche plein de graisse d'un tournevis, tu pourrais très facilement en venir à te blesser sur un de tes couteaux en voulant accéder à un des outils dont tu as besoin.
Bref, tu te rendras compte que ce ne serait vraiment pas une bonne idée 
Les choses sont encore pire avec les espaces de noms, car, comme je te l'ai dit plus haut, il permettent de ranger les "choses qui vont bien ensembles".
Sauf que le terme utilisé pour désigner "quelque chose" est toujours choisi de manière à faire comprendre à celui qui lit le code l'usage auquel ce "quelque chose" sera destiné.
Si bien que le même terme a de très fortes chances d'être utilisé dans différents contextes, qui n'ont pas forcément de liens entre les deux, et pour lesquels le seul moyen d'éviter d'avoir des conflits est ... de ranger correctement ces termes dans un espace de noms.
Prenons un exemple classique :
Tu crées une bibliothèque qui expose une fonction foo(), et, comme tu es un développeur consciencieux, tu va la placer dans ... l'espace de noms "général" de ta bibliothèque sous une forme proche de
1 2 3
| namespace TaBibliothèque{
void foo();
} |
C'est cool, parce qu'elle est "bien rangée" 
De mon coté, je crée une autre bibliothèque, qui n'a absolument rien à voir avec la tienne, mais qui expose aussi une fonction foo, et, comme je suis un développeur consciencieux, vais la placer dans ... l'espace de noms "général" de ma bibliothèque sous une forme proche
1 2 3
| namespace AutreBibliothèque{
void foo();
} |
C'est cool, parce qu'elle est "tout aussi bien rangée" que la tienne 
Voilà que Jules (Henry, Arthur... qui tu veux) décide de développer un projet qui utilise, pour certains aspects de son projet, ta bibliothèque et pour d'autres aspects de son projet ma bibliothèque.
Telle que nous avons prévu les chose, il n'y a aucun conflit possible, car, pour faire appel à ta fonction foo, il devra "ouvrir la boite" de ta bibliothèque, et il y fera donc appel sous la forme dealors que pour faire appel à ma fonction foo, il devra ouvrir la boite de ma bibliothèque, et il y fera donc appel sous la forme de
AutreBibliothèque::foo();
Et même si il veut faire appel aux deux fonctions l'une après l'autre, il n'aura absolument aucun problème.
Sauf que Jules, il est fainéant... Et ca le fait ch**er de devoir écrire à chaque fois TaBibliothèque:: ou AutreBibliothèque::.
Du coup, il va prendre l'habitude, lorsqu'il utilise ta bibliothèque, d'utiliser la directive
using namespace TaBibliothèque;
, et, lorsqu'il va travailler avec ma bibliothèque, d'utiliser la directive
using namespace AutreBibliothèque;
Et tout ira bien "le plus souvent". Jusqu'au jour où il voudra utiliser nos deux bibliothèques en même temps.
Parce que ce jour là, il va faire "comme il fait d'habitude", à savoir écrire un code proche de
1 2 3 4 5 6
| using namespace TaBibliothèque; // pour pouvoir utiliser ta bibliothèque
namespace AutreBibliothèque; // pour pouvoir utiliser ma bibliothèque
void bar(){ // la fonction qu'il veut développer
foo(); // OUCH!!!
foo(); // RE-OUCH!!!
} |
Car le compilateur va lui dire (deux fois, qui plus est) qu'il y a conflit : il est totalement incapable de déterminer "quelle version" de foo() il doit appeler:
- doit-il appeler d'abord la version de ta bibliothèque puis la version de la mienne

- doit-il faire l'inverse

- doit-il appeler deux fois la version de ta bibliothèque

- doit-il appeler deux fois la version de la mienne

Comment veux tu qu'il puisse le savoir !!!
Pire encore! imaginons un programme qui calcule le prix tva comprise sous une forme proche de
1 2 3 4 5 6 7 8 9 10
| #include <iostream>
int main(int argc, char** argv){
int prix_ht = 42;
float tva = 0.2;
int cout = prix_ht * (1. + tva);
std::cout<<cout<<std::endl; // ???
} |
std::cout est -- très clairement la sortie standard, et cout est -- tout aussi clairement -- le "cout tva comprise". Cool!!! il n'y a aucun conflit possible dans ce cas là.
mais, voyons ce que cela donnerait avec la directive using namespace:
Tu aurais donc un code qui ressemblerait )
1 2 3 4 5 6 7 8 9 10 11 12
| #include <iostream>
using namespace std;
int main(int argc, char** argv){
int prix_ht = 42;
float tva = 0.2;
int cout = prix_ht * (1. + tva);
cout<<cout<<endl; // ???
} |
et quand tu vas essayer de le compiler, tu obteindra un message proche d
1 2 3 4 5
| g++ main.cpp
main.cpp: In function int main(int, char**):
main.cpp:11:13: error: invalid operands of types int and <unresolved overloaded function type> to binary operator<<
cout<<cout<<endl; // ???
~~~~~~~~~~^~~~~~ |
WTF??? "cout est de type 'int' et tu lui donnes des paramètres qui conviennent pas". De quoi tomber chèvre.
Tout cela parce qu'il faut savoir que l'opérateur << existe bel et bien pour les données de type int, mais qu'il a un comportement totalement (et qu'il attend un paramètre) différent de l'opérateur << de std::cout :
Ils portent le même noms (operator <<), mais ils n'ont absolument pas la même fonction:
int & opertor << (int & a, int b);
va effectuer un décallage de b bits de vers la gauche de la valeur (binaire) de a alors que
std::ostream & opertor<<(std::ostream & ofs, int a);
va 'injecter" la valeur de a dans le flux ofs.
Et, enfin, endl est la donnée qui, injectée dans un flux de sortie, provoquera le retour à la ligne et le "flush" du flux de sortie. Mais il n'y a -- et c'est une chance !!! -- aucun moyen de transformer cette donnée en valeur numérique entière.
Partager