2.8 – Naming coliziuni și o introducere în namespaces

să presupunem că sunteți de conducere la casa unui prieten pentru prima dată, și adresa dat la tine este 245 Front Street în Mill City. La atingerea Mill City, Vă trageți în sus harta, doar pentru a descoperi că Mill City are de fapt două străzi diferite față de oraș unul de altul! La care ai merge? Cu excepția cazului în care au existat unele indiciu suplimentar pentru a vă ajuta să decideți (de ex. îți amintești casa lui este aproape de râu) ar trebui să-ți suni prietenul și să ceri mai multe informații. Deoarece acest lucru ar fi confuz și ineficient (în special pentru poștașul dvs.), în majoritatea țărilor, toate numele străzilor și adresele caselor dintr-un oraș trebuie să fie unice.

în mod similar, C++ necesită ca toți identificatorii să nu fie ambigui. Dacă doi identificatori identici sunt introduși în același program într-un mod în care compilatorul sau linker-ul nu le poate deosebi, compilatorul sau linker-ul va produce o eroare. Această eroare este denumită în general o coliziune de denumire (sau conflict de denumire).

un exemplu de coliziune de denumire

a.cpp:

1
2
3
4
5
6

#include<iostream>
void myFcn (int x)
{
std:: cout << x;
}

principal.cpp:

1
2
3
4
5
6
7
8
9
10
11

#include<iostream>
void myFcn (int x)
{
std:: cout << 2 * x;
}
int main()
{
întoarcere 0;
}

când compilatorul compilează acest program, acesta va compila a.cpp și principal.cpp independent, și fiecare fișier va compila fără probleme.

cu toate acestea, atunci când linker execută, se va lega toate definițiile în a.cpp și principal.cpp împreună, și de a descoperi definiții contradictorii pentru funcția myFcn. Linker – ul va abandona apoi cu o eroare. Rețineți că această eroare apare chiar dacă myFcn nu este niciodată apelat!

cele mai multe coliziuni de denumire apar în două cazuri:
1) două (sau mai multe) definiții pentru o funcție (sau variabilă globală) sunt introduse în fișiere separate care sunt compilate în același program. Acest lucru va duce la o eroare de linker, așa cum se arată mai sus.
2) două (sau mai multe) definiții pentru o funcție (sau variabilă globală) sunt introduse în același fișier (adesea printr-un #include). Aceasta va duce la o eroare de compilator.

pe măsură ce programele devin mai mari și folosesc mai mulți identificatori, șansele introducerii unei coliziuni de denumire cresc semnificativ. Vestea bună este că c++ oferă o mulțime de mecanisme pentru evitarea coliziunilor de denumire. Domeniul Local, care păstrează variabilele locale definite în interiorul funcțiilor în conflict între ele, este un astfel de mecanism. Dar domeniul local nu funcționează pentru numele funcțiilor. Deci, cum păstrăm numele funcțiilor în conflict între ele?

ce este un spațiu de nume?

revenind la analogia noastră pentru o clipă, a avea două străzi din față a fost doar problematic, deoarece acele străzi existau în același oraș. Pe de altă parte, dacă ar trebui să livrați poștă la două adrese, una la 209 Front Street din Mill City și o altă adresă la 417 Front Street din Jonesville, nu ar exista nicio confuzie cu privire la unde să mergeți. Altfel spus, orașele oferă grupări care ne permit să dezambiguizăm adrese care altfel ar putea intra în conflict între ele. Spațiile de nume acționează ca orașele în această analogie.

un spațiu de nume este o regiune care vă permite să declarați nume în interiorul acestuia în scopul dezambiguizării. Spațiul de nume oferă un domeniu de aplicare (numit domeniul spațiului de nume) numelor declarate în interiorul acestuia-ceea ce înseamnă pur și simplu că orice nume declarat în interiorul spațiului de nume nu va fi confundat cu nume identice în alte domenii.

informații cheie

un nume declarat într-un spațiu de nume nu va fi confundat cu un nume identic declarat într-un alt domeniu.

într-un spațiu de nume, Toate numele trebuie să fie unice, altfel va rezulta o coliziune de denumire.

spațiile de nume sunt adesea folosite pentru a grupa identificatorii înrudiți într-un proiect mare pentru a vă asigura că nu se ciocnesc accidental cu alți identificatori. De exemplu, dacă puneți toate funcțiile matematice într-un spațiu de nume numit matematică, atunci funcțiile dvs. matematice nu se vor ciocni cu funcții cu nume identice în afara spațiului de nume matematic.

vom vorbi despre cum să vă creați propriile spații de nume într-o lecție viitoare.

spațiul de nume global

în C++, orice nume care nu este definit într-o clasă, funcție sau un spațiu de nume este considerat a face parte dintr-un spațiu de nume definit implicit numit spațiu de nume global (uneori numit și domeniul global).

în exemplul din partea de sus a lecției, funcțiile main() și ambele versiuni ale myFcn() sunt definite în spațiul de nume global. Coliziunea de denumire întâlnită în exemplu se întâmplă deoarece ambele versiuni ale myFcn() ajung în spațiul de nume global, ceea ce încalcă regula conform căreia toate numele din spațiul de nume trebuie să fie unice.

spațiul de nume std

când C++ a fost proiectat inițial, toți identificatorii din biblioteca standard C++ (inclusiv std::cin și STD::cout) erau disponibili pentru a fi utilizați fără prefixul std:: (făceau parte din spațiul de nume global). Cu toate acestea, acest lucru a însemnat că orice identificator din biblioteca standard ar putea intra în conflict cu orice nume pe care l-ați ales pentru propriii dvs. identificatori (definit și în spațiul de nume global). Codul care funcționa ar putea avea brusc un conflict de denumire atunci când #a inclus un fișier nou din biblioteca standard. Sau mai rău, programele care ar compila sub o versiune de c++ s-ar putea să nu compileze sub o versiune viitoare de C++, deoarece noii identificatori introduși în biblioteca standard ar putea avea un conflict de denumire cu cod deja scris. Deci, c++ a mutat toate funcționalitățile din biblioteca standard într-un spațiu de nume numit „std” (prescurtare pentru standard).

se pare că numele std::cout nu este chiar std::cout. Este de fapt doar cout, iar std este numele spațiului de nume din care face parte identificatorul cout. Deoarece cout este definit în spațiul de nume std, numele cout nu va intra în conflict cu niciun obiect sau funcții numite cout pe care le creăm în spațiul de nume global.

în mod similar, atunci când accesați un identificator care este definit într-un spațiu de nume (de exemplu , std::cout), trebuie să spuneți compilatorului că căutăm un identificator definit în interiorul spațiului de nume (STD).

cheie insight

când utilizați un identificator care este definit în interiorul unui spațiu de nume (cum ar fi spațiul de nume std), trebuie să spuneți compilatorului că identificatorul trăiește în interiorul spațiului de nume.

există câteva moduri diferite de a face acest lucru.

Explicit namespace qualifier std::

cel mai simplu mod de a spune compilatorului că vrem să folosim cout din spațiul de nume std este prin utilizarea explicită a prefixului std::. De exemplu:

1
2
3
4
5
6
7

#include < iostream >
int main()
{
std:: cout << „Bună ziua lume!”; // când spunem cout, ne referim la cout definit în spațiul de nume std
întoarcere 0;
}

simbolul:: este un operator numit operator de rezoluție a domeniului de aplicare. Identificatorul din stânga simbolului :: identifică spațiul de nume în care este conținut numele din dreapta simbolului::. Dacă nu este furnizat niciun identificator din stânga simbolului::, se presupune spațiul de nume global.

deci, atunci când spunem std::cout, spunem „cout care trăiește în spațiul de nume std”.

acesta este cel mai sigur mod de a utiliza cout, deoarece nu există nicio ambiguitate cu privire la care cout facem referire (cel din spațiul de nume std).

cele mai bune practici

utilizați prefixele spațiului de nume explicite pentru a accesa identificatorii definiți într-un spațiu de nume.

utilizarea spațiului de nume std (și de ce să-l evităm)

o altă modalitate de a accesa identificatorii din interiorul unui spațiu de nume este utilizarea unei instrucțiuni de utilizare a Directivei. Iată programul nostru original „Hello world” cu o directivă folosind:

1
2
3
4
5
6
7
8
9

#include<iostream>
folosind spațiul de nume std; / / aceasta este o directivă folosind spune compilator pentru a verifica spațiul de nume std atunci când rezolvarea identificatori cu nici un prefix
int principal()
{
cout << „Bună ziua lume!”; // cout nu are prefix, deci compilatorul va verifica dacă cout este definit local sau în spațiul de nume STD
return 0;
}

o directivă de utilizare spune compilatorului să verifice un spațiu de nume specificat atunci când încearcă să rezolve un identificator care nu are prefix spațiu de nume. Deci, în exemplul de mai sus, atunci când compilatorul merge pentru a determina ce identificator este cout, acesta va verifica atât local (unde este nedefinit), cât și în spațiul de nume std (unde se va potrivi cu std::cout).

multe texte, tutoriale, și chiar unele compilatoare recomanda sau de a folosi o directivă folosind în partea de sus a programului. Cu toate acestea, utilizate în acest fel, aceasta este o practică proastă, și foarte descurajat.

luați în considerare următorul program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#include < iostream > / / importă declarația std:: cout
folosind spațiul de nume std; / / face STD:: cout accesibil ca „cout”
int cout () / / declară propria noastră funcție „cout”
{
întoarcere 5;
}
int main()
{
cout < < ” Bună ziua, lume!”; / / Eroare de compilare! Ce cout vrem aici? Cel din spațiul de nume std sau cel pe care l-am definit mai sus?
întoarcere 0;
}

programul de mai sus nu compilează, deoarece compilatorul nu poate spune acum dacă dorim funcția cout pe care am definit-o sau cout care este definită în spațiul de nume std.

când se utilizează o directivă de utilizare în acest mod, orice identificator pe care îl definim poate intra în conflict cu orice identificator numit identic în spațiul de nume std. Și mai rău, în timp ce un nume de identificator poate să nu intre în conflict astăzi, acesta poate intra în conflict cu noi identificatori adăugați la spațiul de nume std în viitoarele revizuiri lingvistice. Acesta a fost întregul punct de a muta toți identificatorii din biblioteca standard în spațiul de nume std în primul rând!

avertisment

evitați utilizarea directivelor (cum ar fi utilizarea spațiului de nume std;) în partea de sus a programului. Acestea încalcă motivul pentru care spațiile de nume au fost adăugate în primul rând.

vom vorbi mai multe despre utilizarea declarațiilor (și cum să le folosim în mod responsabil) în lecția 6.12 — utilizarea declarațiilor.

2.9 — Introducere în preprocesor

Index

2.7 — programe cu mai multe fișiere de cod

Lasă un răspuns

Adresa ta de email nu va fi publicată.