Comment utilisez-vous correctement les espaces de noms en C ++?

Je viens d’un arrière-plan Java, où les paquets sont utilisés, pas les espaces de noms. J’ai l’habitude de mettre des classes qui fonctionnent ensemble pour former un object complet dans des packages, puis de les réutiliser ultérieurement à partir de ce package. Mais maintenant je travaille en C ++.

Comment utilisez-vous les espaces de noms en C ++? Créez-vous un espace de noms unique pour l’application entière ou créez-vous des espaces de noms pour les composants principaux? Si oui, comment créez-vous des objects à partir de classes dans d’autres espaces de noms?

Les espaces de noms sont essentiellement des packages. Ils peuvent être utilisés comme ceci:

namespace MyNamespace { class MyClass { }; } 

Puis dans le code:

 MyNamespace::MyClass* pClass = new MyNamespace::MyClass(); 

J’espère que cela pourra aider.

Ou, si vous voulez toujours utiliser un espace de noms spécifique, vous pouvez le faire:

 using namespace MyNamespace; MyClass* pClass = new MyClass(); 

Edit: Suite à ce que dit bernhardrusch , j’ai tendance à ne pas utiliser la syntaxe “using namespace x”, je spécifie généralement explicitement l’espace de nommage lors de l’instanciation de mes objects (le premier exemple que j’ai montré).

Et comme vous l’avez demandé ci – dessous , vous pouvez utiliser autant d’espaces de noms que vous le souhaitez.

Pour éviter de tout dire, Mark Ingram a déjà dit un petit truc pour utiliser les espaces de noms:

Évitez la directive “using namespace” dans les fichiers d’en-tête – cela ouvre l’espace de noms pour toutes les parties du programme qui importent ce fichier d’en-tête. Dans les fichiers d’implémentation (* .cpp), ce n’est normalement pas un gros problème, même si je préfère utiliser la directive “using namespace” au niveau de la fonction.

Je pense que les espaces de noms sont principalement utilisés pour éviter les conflits de noms – pas nécessairement pour organiser votre structure de code. J’organiserais les programmes C ++ principalement avec les fichiers d’en-tête / la structure de fichier.

Parfois, les espaces de noms sont utilisés dans les projets C ++ plus importants pour masquer les détails de l’implémentation.

Note complémentaire à la directive using: Certaines personnes préfèrent utiliser “using” uniquement pour des éléments simples:

 using std::cout; using std::endl; 

Vincent Robert a raison dans son commentaire Comment utilisez-vous correctement les espaces de noms en C ++? .

Utiliser un espace de noms

Les espaces de noms sont utilisés au minimum pour éviter les collisions de noms. En Java, ceci est imposé par le biais de l’idiome “org.domain” (car on suppose que l’on n’utilise rien d’autre que son propre nom de domaine).

En C ++, vous pouvez donner un espace de noms à tout le code de votre module. Par exemple, pour un module MyModule.dll, vous pouvez atsortingbuer à son code l’espace de noms MyModule. J’ai vu ailleurs quelqu’un qui utilise MyCompany :: MyProject :: MyModule. Je suppose que c’est excessif, mais dans l’ensemble, cela me semble correct.

Utiliser “using”

L’utilisation doit être utilisée avec précaution, car elle permet d’importer efficacement un ou tous les symboles d’un espace de noms dans votre espace de noms actuel.

C’est mal de le faire dans un fichier d’en-tête car votre en-tête va polluer chaque source, y compris celle (cela me rappelle les macros …), et même dans un fichier source, mauvais style en dehors d’une scope les symboles de l’espace de noms.

Le moyen le plus sûr d’utiliser “using” est d’importer des symboles sélectionnés:

 void doSomething() { using std::ssortingng ; // ssortingng is now "imported", at least, // until the end of the function ssortingng a("Hello World!") ; std::cout << a << std::endl ; } void doSomethingElse() { using namespace std ; // everything from std is now "imported", at least, // until the end of the function string a("Hello World!") ; cout << a << endl ; } 

Vous verrez beaucoup de "using namespace std;" dans le tutoriel ou des exemples de codes. La raison est de réduire le nombre de symboles pour faciliter la lecture, pas parce que c'est une bonne idée.

"utiliser l'espace de noms std;" est découragé par Scott Meyers (je ne me rappelle pas exactement quel livre, mais je peux le trouver si nécessaire).

Composition de l'espace de noms

Les espaces de noms sont plus que des paquets. Un autre exemple peut être trouvé dans "Le langage de programmation C ++" de Bjarne Stroustrup.

Dans la "Special Edition", à la section 8.2.8 Composition des espaces de noms , il décrit comment fusionner deux espaces de noms AAA et BBB dans un autre appelé CCC. Ainsi, CCC devient un alias à la fois pour AAA et BBB:

 namespace AAA { void doSomething() ; } namespace BBB { void doSomethingElse() ; } namespace CCC { using namespace AAA ; using namespace BBB ; } void doSomethingAgain() { CCC::doSomething() ; CCC::doSomethingElse() ; } 

Vous pouvez même importer des symboles sélectionnés de différents espaces de noms pour créer votre propre interface d'espace de noms personnalisée. Je n'ai pas encore trouvé d'utilisation pratique, mais en théorie, c'est cool.

Je n’en ai vu aucune mention dans les autres réponses, alors voici mes 2 cents canadiens:

Sur la rubrique “using namespace”, une instruction utile est l’alias d’espace de noms, vous permettant de “renommer” un espace de noms, normalement pour lui donner un nom plus court. Par exemple, au lieu de:

 Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::TheClassName foo; Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::AnotherClassName bar; 

tu peux écrire:

 namespace Shorter = Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally; Shorter::TheClassName foo; Shorter::AnotherClassName bar; 

N’écoutez pas tous les gens qui vous disent que les espaces de noms ne sont que des espaces de noms.

Ils sont importants car ils sont considérés par le compilateur pour appliquer le principe de l’interface. En gros, cela peut s’expliquer par un exemple:

 namespace ns { class A { }; void print(A a) { } } 

Si vous vouliez imprimer un object A, le code serait celui-ci:

 ns::A a; print(a); 

Notez que nous n’avons pas explicitement mentionné l’espace de noms lors de l’appel de la fonction. C’est le principe de l’interface: C ++ considère une fonction prenant un type comme argument comme faisant partie de l’interface pour ce type, donc pas besoin de spécifier l’espace de nommage car le paramètre impliquait déjà l’espace de nommage.

Maintenant, pourquoi ce principe est-il important? Imaginez que l’auteur de la classe A n’ait pas fourni de fonction print () pour cette classe. Vous devrez en fournir un vous-même. Comme vous êtes un bon programmeur, vous allez définir cette fonction dans votre propre espace de noms ou peut-être dans l’espace de noms global.

 namespace ns { class A { }; } void print(A a) { } 

Et votre code peut commencer à appeler la fonction print (a) où vous voulez. Maintenant, imaginez que des années plus tard, l’auteur décide de fournir une fonction print (), mieux que la vôtre, car il connaît les éléments internes de sa classe et peut en faire une meilleure version que la vôtre.

Les auteurs de C ++ ont alors décidé que sa version de la fonction print () devrait être utilisée à la place de celle fournie dans un autre espace de nom, afin de respecter le principe de l’interface. Et que cette “mise à jour” de la fonction print () devrait être aussi simple que possible, ce qui signifie que vous ne devrez pas modifier chaque appel à la fonction print (). C’est pourquoi les “fonctions d’interface” (fonction dans le même espace de nommage qu’une classe) peuvent être appelées sans spécifier l’espace de nommage en C ++.

Et c’est pourquoi vous devriez considérer un espace de noms C ++ comme une “interface” lorsque vous en utilisez un et gardez à l’esprit le principe de l’interface.

Si vous voulez une meilleure explication de ce comportement, vous pouvez vous référer au livre Exceptional C ++ de Herb Sutter

Les projets C ++ plus importants que j’ai vus utilisaient rarement plus d’un espace de noms (par exemple, la bibliothèque de boost).

En fait, boost utilise des tonnes d’espaces de noms, généralement chaque partie de boost a son propre espace de nommage pour le fonctionnement interne et peut ensuite ne placer que l’interface publique dans le boost d’espace de noms de premier niveau.

Personnellement, je pense que plus une base de code est grande, plus les espaces de noms deviennent importants, même dans une seule application (ou bibliothèque). Au travail, nous plaçons chaque module de notre application dans son propre espace de noms.

Un autre usage (sans jeu de mots) des espaces de noms que j’utilise beaucoup est l’espace de noms anonyme:

 namespace { const int CONSTANT = 42; } 

C’est fondamentalement la même chose que:

 static const int CONSTANT = 42; 

L’utilisation d’un espace de noms anonyme (au lieu de statique) est toutefois la méthode recommandée pour que le code et les données ne soient visibles que dans l’unité de compilation actuelle en C ++.

Notez également que vous pouvez append un espace de noms. Ceci est plus clair avec un exemple, ce que je veux dire, c’est que vous pouvez avoir:

 namespace MyNamespace { double square(double x) { return x * x; } } 

dans un fichier square.h , et

 namespace MyNamespace { double cube(double x) { return x * x * x; } } 

dans un fichier cube.h Cela définit un espace de noms unique MyNamespace (vous pouvez définir un seul espace de noms sur plusieurs fichiers).

En Java:

 package somepackage; class SomeClass {} 

En C ++:

 namespace somenamespace { class SomeClass {} } 

Et en les utilisant, Java:

 import somepackage; 

Et C ++:

 using namespace somenamespace; 

En outre, les noms complets sont “somepackge.SomeClass” pour Java et “somenamespace :: SomeClass” pour C ++. En utilisant ces conventions, vous pouvez organiser comme vous en avez l’habitude en Java, y compris créer des noms de dossiers correspondants pour les espaces de noms. Les exigences de la classe folder-> package et file-> ne sont cependant pas présentes, vous pouvez donc nommer vos dossiers et classes indépendamment des packages et des espaces de noms.

Vous pouvez aussi contenir “using namespace …” dans une fonction par exemple:

 void test(const std::ssortingng& s) { using namespace std; cout << s; } 

@ marius

Oui, vous pouvez utiliser plusieurs espaces de noms à la fois, par exemple:

 using namespace boost; using namespace std; shared_ptr p(new int(1)); // shared_ptr belongs to boost cout << "cout belongs to std::" << endl; // cout and endl are in std 

[Fév. 2014 - (Cela a-t-il vraiment été si long?): Cet exemple est maintenant ambigu, comme le souligne Joey ci-dessous. Boost et std :: maintenant ont chacun un shared_ptr.]

En règle générale, je crée un espace de noms pour un corpus de code si je pense qu’il pourrait y avoir des conflits de noms de fonctions ou de types avec d’autres bibliothèques. Cela aide aussi à donner un code à la marque, comme un coup de pouce :: .

Je préfère utiliser un espace de noms de premier niveau pour l’application et les sous-espaces de noms pour les composants.

La façon dont vous pouvez utiliser des classes à partir d’autres espaces de noms est étonnamment très similaire à celle de Java. Vous pouvez utiliser “use NAMESPACE”, similaire à une instruction “import PACKAGE”, par exemple, utilisez std. Ou vous spécifiez le paquet comme préfixe de la classe séparée avec “::”, par exemple std :: ssortingng. Ceci est similaire à “java.lang.Ssortingng” en Java.

Notez qu’un espace de noms en C ++ n’est vraiment qu’un espace de noms. Ils ne fournissent aucune des encapsulations faites par les paquets en Java, vous ne les utiliserez probablement pas autant.

J’ai utilisé des espaces de noms C ++ de la même manière que dans C #, Perl, etc. C’est juste une séparation sémantique des symboles entre les éléments de bibliothèque standard, les éléments tiers et mon propre code. Je placerais ma propre application dans un espace de noms, puis un composant de bibliothèque réutilisable dans un autre espace de noms pour la séparation.

Une autre différence entre Java et C ++ est que, en C ++, la hiérarchie des espaces de nommage n’a pas besoin de modifier la disposition du système de fichiers. J’ai donc tendance à placer une bibliothèque entière réutilisable dans un seul espace de noms et des sous-systèmes dans la bibliothèque dans les sous-répertoires:

 #include "lib/module1.h" #include "lib/module2.h" lib::class1 *v = new lib::class1(); 

Je ne mettrais que les sous-systèmes dans des espaces de noms nesteds s’il y avait une possibilité de conflit de noms.