In progetti di dimensioni medio-grandi è possibile che ci siano conflitti tra i nomi esportati tra due differenti librerie (eventualmente provenienti da differenti produttori).
Questo fenomeno è noto come “inquinamento dello spazio dei nomi”.
Il C tenta di risolvere questo problema attraverso l’uso di simboli che “accompagnano” gli identificatori. P.es.:
uso del carattere underscore: _identifier
.
Il C++ offre una metodologia che risolve sistematicamente il problema: il meccanismo dei namespace.
I Namespaces permettono di raggruppare classi, oggetti, funzioni, variabili definendo in pratica un ambiente di visibilità per identificatori globali e variabili globali.
Sono molto utili allorchè ci può essere il rischio di omonima (in particolare tra variabili globali).
A livello di modulo è possibile dichiarare un namespace nel quale vengono poi definiti tutti i simboli del modulo.
studenti
#ifndef __STUDENTI__
#define __STUDENTI__
namespace p1ing {
typedef struct {
int prefisso;
int matricola;
char* Cognome;
char* Nome;
} Studente;
extern Studente Rappresentante;
}
#endif /* __STUDENTI__ */
I nomi raggruppati dal namespace hanno visibilità locale al namespace, in questo senso sono “incapsulati” nel namespace e non sono visibili all’esterno, in questo modo si evitano i conflitti nel caso gli stessi nomi si ripetano nell’ambito del programma.
Il nome del namespace (che a sua volta è un identificativo) è invece un nome globale e può essere usato al di fuori del namespace.
Per poter utilizzare i nomi raggruppati da un namespace, che sono quindi locali al namespace, bisogna esplicitamente “aprire” lo spazio dei nomi, specificando il namespace di cui fanno parte.
L’apertura può essere effettuata in due modi:
1) utilizzando l’operatore binario :: (operatore di risoluzione di visibilità) specificando come operandi il namespace e il nome cui si vuole accedere:
namespace::nome
2) utilizzando l’istruzione :
using namespace nome_namespace;
nel caso in cui tutti i nomi del namespace siano utilizzati con frequenza dal programma
using namespace nome_namespace; può essere utilizzato sia per aprire namespace standard che namespace definiti dal programmatore
Mostra codice Mostra codice Mostra codiceLo standard ISO C++ 1998 ha codificato tutte le librerie standard del C++ all’interno del namespace std.
Per specificare l’inclusione di una libreria standard (ad esempio iostream.h bisogna utilizzare la notazione:
#include<iostream>
avendo tolto il .h
Convenzionalmente, in C++ gli header files i cui nomi terminano con .h non fanno uso dei namespace.
Per includere una libreria del linguaggio C (ad esempio stdlib.h che contiene tra l’altro la funzione system) bisogna utilizzare la notazione:
#include<cstdlib>
avendo quindi aggiunto una c e tolto il .h
Using namespace
stile c
Mostra codiceerrore
Mostra codicestile c++ con risolutore di scope
Mostra codicestile c++ con direttiva using
Mostra codiceUn namespace può contenere sia dichiarazioni che definizioni.
In generale però si usa tenere separate le une dalle altre. In particolare per quanto riguarda le funzioni, esse vengono dichiarate nel namespace ma definite fuori di esso.
In questo è necessario utilizzare l’operatore di risoluzione di visibilità (o l’istruzione using namespace) per qualificare i nomi dei membri definiti altrove (membri esterni).
Possono in ogni caso verificarsi conflitti se:
Nel caso dell’esempio appena visto, di fatto il primo namespace viene ESTESO con il gruppo dei nomi del secondo (rientrano tutti in un unico namespace) anche se si tratta di namespace specificati in header file diversi (NS1.h e NS2.h).
Questa è una regola generale, dovuta al fatto che il nome di un namespace è estendibile.
Ciò consente di suddividere un namespace tra più blocchi e di distribuirlo in maniera differenziata in header file diversi tra moduli clienti diversi, a seconda della parte di interfaccia che è opportuno che vedano.
Per alleviare il problema che sorge nel caso di utilizzo di namespace con lo stesso nome che raggruppano identificativi uguali si può ricorrere alla definizione di sinonimi.
Ovvi motivi suggeriscono in genere di dare nomi corti ai namespace, ma questo aumenta la probabilità di avere namespace con lo stesso nome.
E’ possibile dare ad un namespace un nome elaborato (e quindi “improbabile”) e poi definire un “sinonimo” corto da usare in ambito ristretto con minore probabilità di conflitto con il nome di altri namespace.
I namespace devono essere dichiarati in un ambiente globale, cioè non è possibile dichiarare un namespace all’interno di un blocco, ad esempio all’interno di una funzione.
Tuttavia i namespace possono essere innestati.
1. Strutture e typedef. Record in C/C++: Concetti Base
4. Puntatori a tipi di dato strutturati. Allocazione Dinamica
5. Puntatori: aspetti avanzati
7. Asserzioni
8. Gestione delle eccezioni. Concetti base
9. Programmazione modulare: concetti base
10. Programmazione Modulare: Meccanismi e Strumenti a supporto in C/C++
12. Esercitazione: Strutture Dati Pila e Coda
13. Esercitazione. Strutture Dati: Lista Concatenata
14. Meccanismi di Incapsulamento in C++ Namespaces
15. Programmazione orientata agli oggetti. Introduzione
Da C++ a UML: cap. 25