2.8-Naming collisions and an introduction to namespaces

stel dat u voor het eerst naar het huis van een vriend rijdt, en het adres dat u wordt gegeven is 245 Front Street in Mill City. Bij het bereiken van Mill City, trek je je kaart, alleen om te ontdekken dat Mill City eigenlijk heeft twee verschillende straten aan de overkant van de stad van elkaar! Naar welke zou jij gaan? Tenzij er een extra aanwijzing was om u te helpen beslissen (bijv. je herinnert je dat zijn huis vlakbij de rivier ligt) je zou moeten bellen met je vriend en vragen om meer informatie. Omdat dit verwarrend en inefficiënt zou zijn (vooral voor uw postbode), MOETEN in de meeste landen alle straatnamen en huisadressen binnen een stad uniek zijn.

evenzo vereist C++ dat alle identifiers niet dubbelzinnig zijn. Als twee identieke identifiers in hetzelfde programma worden geà ntroduceerd op een manier dat de compiler of linker ze niet uit elkaar kan houden, zal de compiler of linker een fout produceren. Deze fout wordt over het algemeen aangeduid als een naamgeving botsing (of naamgeving conflict).

Een voorbeeld van een naam botsing

a.cpp:

1
2
3
4
5
6

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

main.cpp:

1
2
3
4
5
6
7
8
9
10
11

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

Wanneer de compiler compileert dit programma compileert a.cpp en de belangrijkste.cpp onafhankelijk, en elk bestand zal compileren zonder problemen.

echter, wanneer de linker wordt uitgevoerd, zal het alle definities in a.cpp en main.cpp samen, en ontdek conflicterende definities voor functie myFcn. De linker zal dan afbreken met een fout. Merk op dat deze fout optreedt ook al wordt myFcn nooit aangeroepen!

de meeste naming botsingen komen in twee gevallen voor:
1) Twee (of meer) definities voor een functie (of globale variabele) worden geïntroduceerd in afzonderlijke bestanden die in hetzelfde programma worden gecompileerd. Dit zal resulteren in een linker fout, zoals hierboven getoond.
2) Twee (of meer) definities voor een functie (of globale variabele) worden in hetzelfde bestand geïntroduceerd (vaak via een #include). Dit zal resulteren in een compiler fout.

naarmate programma ‘ s groter worden en meer identifiers gebruiken, neemt de kans op een naamgevingsbotsing aanzienlijk toe. Het goede nieuws is dat C++ biedt tal van mechanismen voor het vermijden van naamgeving botsingen. Local scope, dat ervoor zorgt dat lokale variabelen die binnen functies zijn gedefinieerd niet met elkaar botsen, is zo ‘ n mechanisme. Maar lokale scope werkt niet voor functienamen. Hoe voorkomen we dat functienamen met elkaar in conflict komen?

Wat is een naamruimte?

even terug naar onze adresanalogie: het hebben van twee Voorstraten was alleen maar problematisch omdat die straten binnen dezelfde stad bestonden. Aan de andere kant, als je post moest leveren aan twee adressen, een op 209 Front Street in Mill City, en een ander adres op 417 Front Street in Jonesville, zou er geen verwarring over waar te gaan. Anders gezegd, steden bieden groepen die ons in staat stellen om adressen te disambigueren die anders met elkaar in conflict zouden kunnen komen. Naamruimten handelen zoals de steden doen in deze analogie.

een naamruimte is een regio die het mogelijk maakt om namen te declareren met als doel disambiguatie. De naamruimte biedt een scope (genaamd naamruimte scope) aan de namen die er binnen zijn gedeclareerd — wat simpelweg betekent dat elke naam die binnen de naamruimte is gedeclareerd niet zal worden verward met identieke namen in andere scopes.

Key insight

een naam die in een naamruimte wordt gedeclareerd, wordt niet verward met een identieke naam die in een andere scope wordt gedeclareerd.

binnen een naamruimte moeten alle namen uniek zijn, anders zal er een naambotsing ontstaan.

naamruimten worden vaak gebruikt om gerelateerde identifiers in een groot project te groeperen om ervoor te zorgen dat ze niet onopzettelijk botsen met andere identifiers. Als je bijvoorbeeld al je wiskundige functies in een naamruimte met de naam math plaatst, dan zullen je wiskundige functies niet botsen met functies met dezelfde naam buiten de naamruimte.

we zullen het hebben over het maken van uw eigen namespaces in een volgende les.

de Globale naamruimte

In C++ wordt elke naam die niet gedefinieerd is binnen een klasse, functie of naamruimte beschouwd als onderdeel van een impliciet gedefinieerde naamruimte die de Globale naamruimte wordt genoemd (soms ook wel het globale bereik genoemd).

in het voorbeeld bovenaan de les worden functies main() en beide versies van myFcn() gedefinieerd in de Globale namespace. De aanname botsing in het voorbeeld gebeurt omdat beide versies van myFcn () in de Globale naamruimte belanden, wat in strijd is met de regel dat alle namen in de naamruimte uniek moeten zijn.

de STD-naamruimte

toen C++ oorspronkelijk was ontworpen, waren alle identifiers in de C++ – standaardbibliotheek (inclusief std::cin en std::cout) beschikbaar om te worden gebruikt zonder het STD:: – voorvoegsel (ze maakten deel uit van de Algemene naamruimte). Dit betekende echter dat elke identifier in de standaardbibliotheek mogelijk in conflict zou kunnen komen met elke naam die u hebt gekozen voor uw eigen identifiers (ook gedefinieerd in de Globale namespace). Code die werkte kan plotseling een naamconflict hebben wanneer je #een nieuw bestand uit de standaardbibliotheek hebt toegevoegd. Of erger nog, Programma ‘ s die onder één versie van C++ zouden compileren, zouden niet onder een toekomstige versie van C++ kunnen compileren, omdat nieuwe identifiers die in de standaardbibliotheek worden geïntroduceerd een naamgevingsconflict kunnen hebben met reeds geschreven code. Dus C++ verplaatste alle functionaliteit in de standaardbibliotheek naar een namespace genaamd “std” (kort voor standaard).

het blijkt dat de naam van std::cout niet echt std::cout is. Het is eigenlijk gewoon cout, en std is de naam van de naamruimte waar de identifier cout deel van uitmaakt. Omdat cout is gedefinieerd in de STD-naamruimte, zal de naam cout niet conflicteren met objecten of functies met de naam cout die we maken in de Globale naamruimte.

wanneer u toegang krijgt tot een identifier die is gedefinieerd in een naamruimte (bijvoorbeeld std::cout), moet u de compiler vertellen dat we op zoek zijn naar een identifier die is gedefinieerd in de naamruimte (std).

Key insight

wanneer u een identifier gebruikt die is gedefinieerd in een naamruimte (zoals de std-naamruimte), moet u de compiler vertellen dat de identifier zich in de naamruimte bevindt.

er zijn een paar verschillende manieren om dit te doen.

expliciete naamruimte qualifier std::

de meest eenvoudige manier om de compiler te vertellen dat we cout willen gebruiken van de STD naamruimte is door expliciet Het STD:: prefix te gebruiken. Bijvoorbeeld:

1
2
3
4
5
6
7

#include <iostream>
int main()
{
std::cout << “Hello world!”; // wanneer we cout zeggen, bedoelen we de cout gedefinieerd in de STD naamruimte
return 0;
}

het:: symbool is een operator die scope resolution operator wordt genoemd. De identifier links van het :: symbool identificeert de naamruimte waarin de naam rechts van het:: symbool zich bevindt. Als er geen identifier links van het:: symbool is opgegeven, wordt de Algemene naamruimte aangenomen.

dus als we std::cout zeggen, zeggen we “the cout that lives in namespace std”.

dit is de veiligste manier om cout te gebruiken, omdat er geen onduidelijkheid is over welke cout we refereren (die in de STD-naamruimte).

Best practice

gebruik expliciete naamruimtevoorvoegsels om toegang te krijgen tot identifiers die in een naamruimte zijn gedefinieerd.

een andere manier om toegang te krijgen tot identifiers in een namespace is het gebruik van een using directive statement. Hier is ons originele “Hello world” programma met een gebruiksrichtlijn:

1
2
3
4
5
6
7
8
9

#include <iostream>
using namespace std; // dit is een richtlijn vertelt de compiler om te controleren of de std-naamruimte bij het omzetten van id ‘ s met geen prefix
int main()
{
cout << “Hello world!”; // cout heeft geen prefix, dus de compiler zal controleren of cout lokaal gedefinieerd is of in naamruimte std
return 0;
}

een gebruiksdirectory vertelt de compiler om een opgegeven naamruimte te controleren wanneer wordt geprobeerd een identifier zonder naamruimtevoorvoegsel om te zetten. Dus in het bovenstaande voorbeeld, wanneer de compiler gaat om te bepalen welke identifier cout is, zal het zowel lokaal controleren (waar het niet gedefinieerd is) en in de STD naamruimte (waar het zal overeenkomen met std::cout).

veel teksten, tutorials, en zelfs sommige compilers raden aan of gebruiken een using directive aan de bovenkant van het programma. Echter, gebruikt op deze manier, dit is een slechte praktijk, en zeer ontmoedigd.

overweeg het volgende programma:

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

#include <iostream> // invoer van de verklaring van std::cout
using namespace std; // maakt std::cout toegankelijk als “rechter”
int cout() // verklaart onze eigen “rechter” – functie
{
terug 5;
}
int main()
{
cout << ” Hallo, wereld!”; / / Compileerfout! Welke cout willen we hier? Die in de STD-naamruimte of die we hierboven hebben gedefinieerd?
return 0;
}

het bovenstaande programma compileert niet, omdat de compiler nu niet kan zeggen of we de cout-functie willen die we hebben gedefinieerd, of de cout die is gedefinieerd in de STD-naamruimte.

wanneer een using directive op deze manier wordt gebruikt, kan elke identifier die we definiëren conflicteren met elke identifier met dezelfde naam in de STD-naamruimte. Erger nog, hoewel een identifiernaam vandaag niet conflicteert, kan het conflicteren met nieuwe identifiers toegevoegd aan de STD-naamruimte in toekomstige taalrevisies. Dit was het hele punt van het verplaatsen van alle identifiers in de standaard bibliotheek naar de STD naamruimte in de eerste plaats!

waarschuwing

vermijd het gebruik van richtlijnen (zoals het gebruik van namespace std;) aan de bovenkant van uw programma. Ze schenden de reden waarom namespaces zijn toegevoegd in de eerste plaats.

we zullen het meer hebben over het gebruik van statements (en hoe ze verantwoord te gebruiken) in Les 6.12 — het gebruik van statements.

2.9 — Inleiding tot de preprocessor

zaakregister

2.7 — programma ‘ s met meerdere codebestanden

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.