Version en ligne

Tutoriel : Les espaces de noms (namespace)

Table des matières

Les espaces de noms (namespace)
Mais qu'est-ce donc qu'un espace de noms ?
Mon namespace à moi ...
L'utilisation et le mot-clé using
Espace de noms anonyme, le nouveau static

Les espaces de noms (namespace)

Mais qu'est-ce donc qu'un espace de noms ?

Alors, les espaces de noms, qu'est-ce que c'est ? À quoi ça peut bien servir ? Je vais essayer de vous expliquer le plus clairement possible comment créer vos propres espaces de noms, comment les utiliser, mais également leur utilité.

Vous en avez tous déjà utilisé un. En effet, les fonctions d'entrée-sortie standard du C++ sont situées dans un espace de noms : std. Vous l'utilisez chaque fois que vous utilisez les fonctions "cin" et "cout" par exemple, mais savez-vous pourquoi on utilise un espace de noms ?

Si vous ne savez pas, alors vous êtes au bon endroit !

Mais qu'est-ce donc qu'un espace de noms ?

Mon namespace à moi ...

Je ne peux pas commencer à vous expliquer comment les utiliser sans démarrer par la base : qu'est-ce que c'est, et pourquoi peut-on en avoir besoin ?

La réponse est simple : un espace de noms, c'est quelque chose qui regroupe des variables et des fonctions, des classes, tout ce que vous voulez dans un même ensemble. Je vais vous présenter l'utilité d'un espace de noms et pour le faire, rien de mieux qu'un exemple :

Fichier main.cpp :

#include <iostream>
 
using namespace std; //On utilise un espace de noms ici
 
const int valeur=5;
 
int main(int argc, char* argv[])
{
      cout << "Valeur = " << valeur << endl;
      return 0;
}

Fichier second.cpp :

const int valeur=2;
//Plein de fonctions

Comme vous le voyez ici, dans le fichier main.cpp, on déclare une variable globale valeur. Et dans le fichier second.cpp, on déclare également une variable globale valeur. Si on compile notre projet avec ces deux fichiers, on aura le droit à une magnifique erreur nous disant que "valeur" à déjà été définie dans main.cpp.

Oui mais il ne sert à rien ton exemple. On n'a pas idée de déclarer deux variables globales avec le même nom. Et en plus on sait très bien qu'il faut éviter les globales. Pourquoi tu nous montres ça ?

Bonne question. Il s'agit d'un exemple bête pour montrer le principe, je ne vais pas écrire tout un code complexe pour montrer leur utilité si je peux le faire avec un code tout simple et accessible à tous.

Revenons donc au sujet : dans mon exemple, le compilo provoque une erreur car la variable est déclarée deux fois avec le même nom. Comment contourner ce problème ? Il suffit d'utiliser un espace de noms. Si les deux variables sont situées dans deux espaces de noms différents, elles peuvent très bien avoir le même nom, le compilo sait laquelle des deux variables il doit choisir car on lui dira "je veux la variable située dans tel espace de noms".

L'intérêt de base est ici. Evidemment, dans l'exemple, c'est assez stupide d'utiliser un espace de noms dans un tel programme. Mais maintenant imaginez que vous soyez en train de coder une bibliothèque. Vous ne pouvez pas vous permettre de "bloquer" l'utilisation de certains noms à l'utilisateur de votre bibliothèque. Exemple : sans espace de noms, si dans votre biblio vous utilisez une classe Event, l'utilisateur n'aura pas le droit de créer de classe Event, ce qui impose au passage que l'utilisateur connaisse la composition du code source de votre bibliothèque pour savoir quel mot-clé il n'a pas le droit d'utiliser.
Avec un namespace, le problème est contourné. On crée un namespace pour sa bibliothèque, et voilà le travail, l'utilisateur peut utiliser tout les noms qu'il veut, il ne risque pas le conflit. La SFML (pour ne citer qu'elle) utilise ce système. Elle est écrite dans un namespace : sf.

Maintenant que vous comprenez un peu mieux leur utilité, voyons comment on peut utiliser notre propre namespace.


Mon namespace à moi ...

Mon namespace à moi ...

Mais qu'est-ce donc qu'un espace de noms ? L'utilisation et le mot-clé using

Si vous lisez ces lignes, c'est que leur utilisation vous intéresse, et je vous rassure, elle n'a rien de bien difficile. Il suffit juste de savoir comment créer son propre espace de noms et le tour est joué.

Entrons directement dans le vif du sujet. Pour créer un namespace, rien de plus simple :

namespace NomDuNamespace
{
        //Contenu du namespace
}

Prenez note du fait qu'il n'y a pas de point-virgule après l'accolade fermante.

Je la mets où ma déclaration, et je mets quoi dedans ?

Les déclarations de namespace se mettent dans les fichiers header (.h ou .hpp) et dans les fichiers source (.cpp). Pour ce qui est du contenu, encore une fois on reste dans la simplicité, vous mettez ce que vous mettriez habituellement dans votre fichier .h, .hpp ou .cpp. Rien de tel qu'un exemple pour comprendre :

Fichier perso.hpp :

#ifndef PERSO_HPP
#define PERSO_HPP
 
namespace Vanger
{
        class Perso
        {
        private:
                //Mes attributs
 
        public:
                //Le prototype de mes méthodes
        };
 
        //Le prototype des mes fonctions
        int carre(int);
 
        //Des variables
        int valeur=5;
} //Fin du namespace
 
#endif

Fichier perso.cpp :

#include "perso.hpp"
 
namespace Vanger
{
        //Mes fonctions, méthodes
        Perso::Perso()
        {
                std::cout << "Creation d'un perso" << std::endl;
        }
 
        int carre(int valeur)
        {
                return valeur*valeur;
        }
} //Fin du namespace

Comme vous le voyez, rien ne change, on se contente dans les fichiers .hpp d'écrire nos classes, nos prototypes de fonctions, nos variables (et l'attribution de leur valeur). Et dans le fichier .cpp, on écrit le corps de nos fonctions, méthodes, etc. Comme vous le feriez normalement. La seule différence réside dans le fait que vous le faites à l'intérieur d'un bloc "namespace Nomdevotrename".

Vanger::valeur=10;

Dans l'exemple je crée donc un namespace Vanger qui contient une fonction carre, une variable valeur et une classe Perso.

Ainsi, utiliser le mot clef private dans un namespace comme ceci, cela ne fonctionne pas :

namespace Vanger
{
      private const int valeur; //Tente de rendre privée la variable valeur
}

Les éléments d'un namespace sont obligatoirement publics. Attention également, vous ne pouvez pas dans votre fichier .cpp écrire une fonction comme ceci, car le corps d'une fonction doit être écrit à l'intérieur du namespace :

int Vanger::carre(int valeur)
{
        return valeur*valeur;
}

Votre compilateur refusera de créer l'exécutable dans ces cas-là. Si vous vous demandez ce qu'est le "Vanger::" devant la fonction carre, c'est tout simplement le moyen d'accéder au contenu du namespace Vanger, comme nous allons le voir dès maintenant.


Mais qu'est-ce donc qu'un espace de noms ? L'utilisation et le mot-clé using

L'utilisation et le mot-clé using

Mon namespace à moi ... Espace de noms anonyme, le nouveau static

C'est bien beau de créer un espace de noms, encore faut-il savoir y accéder. Et là encore, tout le monde sait le faire car tout le monde l'a déjà fait au moins une fois dans son passé de codeur C++. On le fait régulièrement avec le namespace std (standard au C++).

Pour accéder, rien de plus simple :

int x=Vanger::carre(3);

Dans cet exemple, je fais appel à la fonction carre située dans l'espace de noms que j'ai créé dans la partie précédente. Comme vous le voyez, j'ai juste eu à ajouter "NomduNamespace::" devant l'appel à la fonction et le tour est joué. Le système est exactement le même pour accéder à une classe ou à une variable.

int x=Vanger::valeur;

Un autre moyen d'utiliser un namespace est le mot-clé "using". Avec celui-ci, vous n'avez plus à écrire le nomdunamespace:: devant chaque appel aux fonctions d'un espace de noms donné.
Vous l'utilisez tous très souvent comme ceci :

using namespace std;

Vous placez généralement cette ligne avant le main. Ce qu'il faut savoir, c'est que vous pouvez la placer n'importe où. Elle agira alors dans le bloc où elle à été placée. Si vous la mettez en dehors de tout bloc (par exemple avant le main), elle agira comme une variable globale statique (c'est-à-dire sur l'ensemble du fichier dans lequel elle a été déclarée), mais si vous la placez dans un bloc "if" par exemple, la condition ne sera valable que pour le if en question.

int x=0;
bool continuer=true;
if(continuer)
{
     using namespace Vanger;
     x+=carre(x); //Ici, le programme va chercher carre dans le namespace Vanger
}
else
{
     x+=carre(x); //Erreur du compilo : fonction carre non déclarée.
}

Dans le premier bloc du if, je déclare travailler dans le namespace Vanger, donc le programme va chercher la fonction carre dans ce namespace. Dans le deuxième bloc, il ne sait pas où chercher car le "using namespace Vanger" n'est pas valable pour ce bloc. Résultat : la fonction carre n'est pas déclarée.

Ce n'est pas le cas pour les variables. Ainsi, il est tout à fait possible, dans le cas du namespace Vanger toujours, d'écrire un main comme ceci :

#include <iostream>
#include "vanger.h"
 
using namespace std;
using namespace Vanger;
 
int main(int argc, char* argv[])
{
    int valeur=8;
    cout << valeur << endl;
    return 0;
}

Je peux donc redéfinir une variable "valeur" bien que celle-ci existe dans le namespace. Dans le "cout" qui suit, la variable valeur utilisée étant celle définie dans le main et non celle du namespace. Les variables situées dans le namespace sont utilisées en dernier recours si le compilateur ne trouve pas la variable dans le bloc en cours ou un bloc parent (si vous avez du mal à comprendre, vous pouvez revoir la portée des variables).

Voilà, vous savez utiliser votre espace de noms ainsi que le mot-clé "using".


Mon namespace à moi ... Espace de noms anonyme, le nouveau static

Espace de noms anonyme, le nouveau static

L'utilisation et le mot-clé using

Dans cette petite partie, je vais aborder les espaces de noms anonymes. Pourquoi est-ce qu'on les appelle anonymes ? Tout simplement parce qu'ils sont définis sans nom. Ces namespaces particuliers s'écrivent directement dans le fichier où ils seront utilisés. Un exemple vaut bien mieux qu'un long discours :

#include <iostream>
 
using namespace std;
 
namespace
{
    int carre(int valeur)
    {
        return valeur*valeur;
    }
    int valeur=5;
}
 
int main(int argc, char* argv[])
{
    cout << carre(valeur) << endl;
    return 0;
}

Pour définir l'espace de noms, c'est toujours le même système. La seule différence réside dans le fait que celui-ci n'a plus de nom.

Mais si il n'a plus de nom, comment on fait pour accéder à ce qu'il contient ? On ne peut plus faire NomDuNamespace:: ? Et pour le mot-clé using ?

On ne les utilise tout simplement pas. Son contenu est directement accessible comme vous pouvez le voir dans le code précédent. Il me suffit d'appeler la fonction "carre" et le compilateur va la chercher dans le namespace anonyme. MAIS, son contenu n'est disponible que dans le fichier où celui-ci à été déclaré, et là est tout l'intérêt.

Il s'agit en réalité du même comportement que celui dû à l'utilisation du mot-clé static (qui permet de limiter la portée d'une variable au fichier où elle a été déclarée). Seulement, dans le langage C++, l'utilisation de namespace anonyme est privilégiée sur l'utilisation du mot-clé static.

Ce tutoriel sur les namespaces touche à sa fin. Vous savez désormais comment créer et utiliser vos propres espaces de noms. Sachez également qu'on peut très bien imbriquer des espaces de noms les uns dans les autres de la même façon qu'expliquée dans ce tutoriel, la seule chose importante est d'être bien organisé.

Et en parlant d'être organisé, vous l'aurez compris maintenant, le principe d'un espace de noms c'est d'organiser son code. Et maintenant, vous pouvez le faire.

Bon codage à vous tous et j'espère que ce tutoriel pourra vous servir un jour !


L'utilisation et le mot-clé using