web-dev-qa-db-fra.com

Espace de noms C ++ et inclure

Pourquoi avons-nous besoin à la fois d'utiliser l'espace de noms et d'inclure des directives dans les programmes C++?

Par exemple,

#include <iostream>

using namespace std;

int main() {
 cout << "Hello world";
}

Pourquoi ne suffit-il pas d'avoir juste #include ou simplement d'avoir "using namespace std" et de se débarrasser de l'autre?

(Je pense à une analogie avec Java, importez Java.net. * Importera tout importera de Java.net, vous n'avez rien d'autre à faire.)

30
duli

Dans C++ les concepts sont séparés. C'est par conception et utile.

Vous pouvez inclure des éléments qui, sans espaces de noms, seraient ambigus.

Avec les espaces de noms, vous pouvez faire référence à deux classes différentes qui portent le même nom. Bien sûr, dans ce cas, vous n'utiliseriez pas la directive using ou si vous le faisiez, vous devrez spécifier l'espace de noms des autres éléments dans l'espace de noms que vous souhaitez.

Notez également que vous n'avez PAS BESOIN de l'utilisation - vous pouvez simplement utiliser std :: cout ou tout ce dont vous avez besoin pour accéder. Vous faites précéder les éléments de l'espace de noms.

28
Tim

utiliser des directives et inclure des directives de préprocesseur sont deux choses différentes. include correspond approximativement à la variable d'environnement CLASSPATH de Java, ou à la -cp option de la machine virtuelle Java.

Ce qu'il fait, c'est de faire connaître les types au compilateur. Incluant simplement <string> par exemple, vous permettra de vous référer à std::string:

#include <string>
#include <iostream>

int main() {
    std::cout << std::string("hello, i'm a string");
}

Maintenant, l'utilisation des directives est comme import en Java. Ils rendent les noms visibles dans la portée dans laquelle ils apparaissent, vous n'avez donc plus besoin de les qualifier complètement. Comme en Java, les noms utilisés doivent être connus avant de pouvoir être rendus visibles:

#include <string> // CLASSPATH, or -cp
#include <iostream>

// without import in Java you would have to type Java.lang.String . 
// note it happens that Java has a special rule to import Java.lang.* 
// automatically. but that doesn't happen for other packages 
// (Java.net for example). But for simplicity, i'm just using Java.lang here.
using std::string; // import Java.lang.String; 
using namespace std; // import Java.lang.*;

int main() {
    cout << string("hello, i'm a string");
}

C'est une mauvaise pratique d'utiliser une directive using dans les fichiers d'en-tête, car cela signifie que tous les autres fichiers source qui l'incluent verront ces noms en utilisant la recherche de nom non qualifié. Contrairement à Java, où vous ne rendez visible que les noms du package dans lequel apparaît la ligne d'importation, en C++, cela peut affecter l'ensemble du programme, s'ils incluent ce fichier directement ou indirectement.

Soyez prudent lorsque vous le faites à l'échelle mondiale, même dans les fichiers d'implémentation. Mieux vaut les utiliser le plus localement possible. Pour l'espace de noms std, je ne l'utilise jamais. Moi et beaucoup d'autres personnes, j'écris toujours std:: devant les noms. Mais si vous le faites, faites-le comme ceci:

#include <string>
#include <iostream>

int main() {
    using namespace std;
    cout << string("hello, i'm a string");
}

Pour savoir quels sont les espaces de noms et pourquoi vous en avez besoin, veuillez lire la proposition que Bjarne Stroustrup a faite en 1993 pour les ajouter à la prochaine norme C++. C'est bien écrit:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1993/N0262.pdf

39

En C++ #include et l'utilisation ont des fonctions différentes.

#include place le texte du fichier inclus dans votre fichier source (en fait nité de traduction ), les espaces de noms en revanche ne sont qu'un mécanisme pour avoir des noms uniques afin que différentes personnes puissent créer un objet "foo" .

Cela vient du C++ n'ayant pas le concept d'un module.

Gardez à l'esprit que les espaces de noms en C++ sont ouverts, ce qui signifie que différents fichiers peuvent définir différentes parties du même espace de noms (un peu comme les classes partielles de .NET).

//a.h
namespace eg {
    void foo();
}

//b.h
namespace eg {
    void bar();
}
8
Motti

L'inclusion définit l'existence des fonctions.

L'utilisation facilite leur utilisation.

cout tel que défini dans iostream est en fait nommé "std :: cout".

Vous pourriez éviter d'utiliser l'espace de noms en écrivant.

std::cout << "Hello World";
6
James Curran

Ces mots clés sont utilisés à des fins différentes.

Le mot-clé using rend un nom à partir d'un espace de noms disponible pour une utilisation dans la région déclarative actuelle. C'est surtout pour plus de commodité afin que vous n'ayez pas à utiliser le nom complet tout le temps. Cette page l'explique en détail.

L'instruction #include est une directive de préprocesseur et elle indique au préprocesseur de traiter le contenu d'un fichier spécifié comme si ce contenu était apparu dans le programme source au point où la directive apparaît. Autrement dit, vous pouvez considérer cette instruction comme la copie du fichier inclus dans celui en cours. Le compilateur compile ensuite le fichier entier comme si vous aviez écrit tout le code dans un gros fichier.

4
Vincent Ramdhanie

Je pense que les autres réponses manquent un peu. Dans tout C++, Java et C #, la chose using/import est entièrement facultative. Ce n'est donc pas différent.

Et puis vous devez faire autre chose pour que le code soit visible de toute façon, sur les trois plates-formes.

En C++, vous devez au minimum l'inclure dans l'unité de traduction actuelle (assez bon pour de nombreuses implémentations de vecteur, chaîne, etc.), souvent vous devez également ajouter quelque chose à votre éditeur de liens, bien que certaines bibliothèques le fassent automatiquement en fonction de la inclure (par exemple, booster lors de la construction sous Windows).

Et en C # vous devez ajouter une référence à l'autre Assembly. Cela prend en charge l'équivalent des paramètres d'inclusion et de lien.

Et dans Java vous devez vous assurer que le code est sur le chemin de classe, par exemple en y ajoutant le pot correspondant.

Il y a donc des choses très étroitement similaires requises sur les trois plates-formes, et la séparation entre using/import (une commodité) et la résolution de liaison réelle (une exigence) est la même dans les trois.

4
Daniel Earwicker

Vous devez comprendre espaces de noms si vous voulez vraiment comprendre cela.

Avec include, vous incluez simplement le fichier d'en-tête.

Avec using namespace vous déclarez que vous utilisez un espace de noms donné qui contient des éléments tels que cout. donc si vous faites ceci:

using namespace std;

vous utilisez cout vous pouvez simplement faire

cout << "Namespaces are good Fun";

au lieu de:

std::cout << "Namespaces are Awesome";

Notez que si vous ne le faites pas #include <iostream> vous ne pourrez ni utiliser ni std::cout ni cout dans vos déclarations, etc. parce que vous n'incluez pas l'en-tête.

3
JohnIdol

Comme indiqué, C++ et Java sont des langages différents et font des choses quelque peu différentes. De plus, C++ est plus un langage "plaisantant" et Java plus un langage conçu.

Bien que using namespace std; Ne soit pas nécessairement une mauvaise idée, son utilisation pour tous les espaces de noms éliminera tout l'avantage. Les espaces de noms existent pour que vous puissiez écrire des modules sans tenir compte des conflits de noms avec d'autres modules, et using namespace this; using namespace that; Peut créer des ambiguïtés.

3
David Thornley

En C++, la directive include copiera et collera le fichier d'en-tête dans votre code source à l'étape de prétraitement. Il convient de noter qu'un fichier d'en-tête contient généralement des fonctions et des classes déclarées dans un espace de noms. Par exemple, le <vector> l'en-tête pourrait ressembler à quelque chose comme ceci:

namespace std {
    template <class T, class Allocator = allocator<T> > class vector;
    ...
} 

Supposons que vous ayez besoin de définir un vecteur dans votre fonction principale, vous faites #include <vector> et vous avez maintenant le morceau de code ci-dessus dans votre code:

namespace std {
    template <class T, class Allocator = allocator<T> > class vector;
    ...
}
int main(){
   /*you want to use vector here*/
}

Notez que dans votre code, la classe vector se trouve toujours dans l'espace de noms std. Cependant, votre fonction principale se trouve dans l'espace de noms global par défaut, donc simplement l'inclusion de l'en-tête ne rendra pas la classe vectorielle visible dans l'espace de noms global. Vous devez utiliser using ou faire un préfixe comme std::vector.

2
user2859314

Même Stroustrup qualifie le mécanisme #include De quelque peu hack. Cependant, cela rend la compilation séparée beaucoup plus facile (expédiez les bibliothèques et les en-têtes compilés au lieu de tout le code source).

La question est vraiment "pourquoi le C++ - alors qu'il avait déjà le mécanisme #include - a-t-il ajouté des espaces de noms?"

Le meilleur exemple que je connaisse sur les raisons pour lesquelles #include Ne suffit pas vient de Sun. Apparemment, les développeurs de Sun ont eu des problèmes avec l'un de leurs produits car ils avaient écrit une fonction mktemp() qui avait la même signature qu'une fonction mktemp() qui était incluse via un fichier qui était lui-même inclus dans un en-tête que le projet voulait réellement.

Bien entendu, les deux fonctions n'étaient pas compatibles et l'une ne pouvait pas être utilisée en remplacement de l'autre. D'un autre côté, le compilateur et l'éditeur de liens ne s'en sont pas rendu compte lors de la construction du binaire, et parfois mktemp() appelait une fonction, et parfois il en appelait une autre, en fonction de l'ordre dans lequel différents fichiers étaient compilés ou liés.

Le problème vient du fait que C++ était à l'origine compatible avec - et essentiellement superposé au dessus de - C. Et C n'a qu'un espace de noms global. C++ a résolu ce problème - dans un code qui n'est pas compatible avec C - via des espaces de noms.

C # et Java (1) ont un mécanisme d'espace de noms (namespace en C #, package en Java), (2) sont généralement développés via des IDE qui gèrent faisant référence aux binaires pour le développeur, et (3) n'autorisent pas les fonctions autonomes (une portée de classe est en quelque sorte un espace de noms, et réduit le risque de polluer l'espace de noms global) afin qu'ils aient une solution différente à ce problème. Cependant, il il est toujours possible d'avoir une certaine ambiguïté concernant la méthode que vous appelez (disons, un conflit de noms entre deux interfaces dont une classe hérite), et dans ce cas, les trois langages nécessitent que le programmeur spécifie clairement la méthode qu'ils recherchent réellement , généralement en ajoutant le nom de la classe/interface parent.

2
Max Lybbert

Un liner (pas que ce soit quelque chose de nouveau :)):

en utilisant std vous permet d'omettre le préfixe std ::, mais vous ne pouvez pas utiliser cout sans iostream.

2
PolyThinker