Standardul C++ a introdus in 1995 un mecanism de prevenire a unor astfel de coliziuni: cuvantul cheie namespace. Fiecare multime de definitii dintr-o librarie sau program este impachetat intr-un spatiu de nume; daca alte definitii au nume identic, dar sunt situate in alte spatii de nume, nu apare coliziune.
Libraria standard C++ este impachetata in spatiul de nume std. Directiva using expune toate elementele unui spatiu de nume in unitatea de traducere din care a fost apelata. De exemplu:
using namespace std; //neindicat! expunde toate numele din std!
Se indica folosirea declaratiei using:
using std::cout;
Exemplul 1: O clasa vector poate poate aparea simultan intr-o librarie matematica si intr-o librarie de containere, rezultand conflict. Sunt in conflict, de asemenea, si functiile membru cu nume identic (de exemplu, functiile care supraincarca operatorii).
//file excelSoftCompany.h
namespace excel{
class string {/**/};
class vector {/**/};
}
//file excelSoftCompany.cpp
#include <string>
#include "excelSoftCompany.h"
int main(){
using namespace excel;
string s; //spatiul de nume excel
std::string s1; //spatiul de nume std
return 0;
}
Se observa proprietatea de nume complet calificat: spatiu::clasa::data_membru.
ALGORITMUL LUI KOENIG
Acest algoritm instruieste compilatorul sa cerceteze si spatiul de nume care contine tipul argumentului unei functii; este solutia standardizata care rezolva situatii de genul:
namespace MINE{
class C{/**/};
void func(C);
}
MINE::C ob; //obiect global de tipul MINE::C
int main(){
func(ob); //OK, MINE::func() a fost apelata, desi nu este expusa; de ce?
return 0;
}
Fara acest algoritm, un programator nu poate decat sa repete numele calificat al functiei sau sa utilizeze directiva using.
#include <iostream>
using std::cout;
int main(){
cout << "hello";
return 0;
}
Operatorul supraincarcat << nu este membru al cout, ci functie friend in spatiul std. Fara algoritmul lui Koenig, ar trebui scris: std::operator<<(cout,"hello");!
Observatii:
1. Algoritmul este aplicat automat si nu poate fi blocat!
2. Rezolutia in spatiile de nume este rezolvata in mod static (la compilare), deci nu implica o suprasarcina la executie.
#include <iostream>
namespace NS1{
class B{/**/};
void f(B b){cout << "a";};
};
void f(NS1::B b){cout << "b";};
int main(){
NS1::B ob;
f(ob);
return 0;
}
Un compilator care respecta standardul raspunde la acest text sursa cu un mesaj de eroare deoarece exista ambiguitate (nu se poate decide care versiune a functiei f() se executa: cea globala sau cea din NS1, regasita automat de catre algoritmul lui Koenig). Un compilator ne-standard alege pur si simplu o versiune a lui f()!
Restrictii
Spatiul de nume standard std nu poate fi modificat cu declaratii aditionale! Se interzice supraincarcarea operatorilor new si delete intr-un spatiu de nume!
char *pc; //global
namespace A{
void* operator new (std::size_t);
void operator delete (void*);
void f(){
pc = new char('a'); //se utilizeaza A::new
}
}
void g(){
delete pc; // se apeleaza A::delete sau ::delete?
}
Unii programatori pot considera ca trebuie sa se apeleze perechea lui A::new, A::delete; altii considera ca trebuie sa se apeleze operatorul standard delete deoarece A::delete nu este vizibil in g! Interzicand supraincarcarea operatorilor in interiorul spatiilor de nume, se evita astfel de ambiguitati.