2.8 – Pojmenování kolize a úvod do jmenných prostorů

řekněme, že jedete do domu přítele poprvé, a přednáší na vás je 245 Front Street v Mill City. Po dosažení Mill City, vytáhnete mapu, jen abyste zjistili, že Mill City má ve skutečnosti dvě různé přední ulice napříč městem od sebe! Ke kterému byste šel? Pokud tam byly nějaké další vodítko, které vám pomohou rozhodnout (např pamatujete si, že jeho dům je blízko řeky) budete muset zavolat svému příteli a požádat o další informace. Protože by to bylo matoucí a neefektivní (zejména pro vašeho pošťáka), ve většině zemí musí být všechny názvy ulic a adresy domů ve městě jedinečné.

podobně C++ vyžaduje, aby všechny identifikátory nebyly nejednoznačné. Pokud jsou do stejného programu vloženy dva identické identifikátory způsobem, který kompilátor nebo linker nedokáže rozeznat, kompilátor nebo linker způsobí chybu. Tato chyba je obecně označována jako kolize pojmenování (nebo konflikt pojmenování).

příklad pojmenování kolize

a.cpp:

1
2
3
4
5
6

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

hlavní.cpp:

1
2
3
4
5
6
7
8
9
10
11

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

Když kompilátor sestavuje tento program bude kompilovat a.cpp a hlavní.cpp nezávisle, a každý soubor bude kompilovat bez problémů.

když se však linker spustí, propojí všechny definice v a.cpp a hlavní.cpp společně, a objevit konfliktní definice pro funkci myFcn. Linker pak přeruší s chybou. Všimněte si, že k této chybě dochází, i když myFcn není nikdy volán!

Většina pojmenování kolize nastat ve dvou případech:
1), Dva (nebo více) definice funkce (nebo globální proměnné) jsou zavedeny do samostatných souborů, které jsou kompilovány do stejného programu. To bude mít za následek chybu linker, jak je uvedeno výše.
2) Dvě (nebo více) definice pro funkci (nebo globální proměnné) jsou zavedeny do stejného souboru (často přes # include). To bude mít za následek chybu kompilátoru.

jak se programy zvětšují a používají více identifikátorů, pravděpodobnost zavedení kolize pojmenování se výrazně zvyšuje. Dobrou zprávou je, že C++ poskytuje spoustu mechanismů, jak se vyhnout kolizím pojmenování. Jedním z takových mechanismů je lokální rozsah, který udržuje lokální proměnné definované uvnitř funkcí před vzájemným konfliktem. Ale místní rozsah nefunguje pro názvy funkcí. Jak tedy zabráníme vzájemnému konfliktu názvů funkcí?

co je jmenný prostor?

zpět na naši adresu analogie na okamžik, mít dvě přední ulice bylo problematické pouze proto, že tyto ulice existovaly ve stejném městě. Na druhou stranu, pokud jste měli doručit mail na dvě adresy, jednu na 209 Front Street v Mill City, a jiné adresu, na 417 Front Street v Jonesville, tam by byl žádný zmatek o tom, kam jít. Jinak řečeno, města poskytují seskupení, která nám umožňují disambiguovat adresy, které by se jinak mohly navzájem konfliktovat. Jmenné prostory fungují jako města v této analogii.

jmenný prostor je oblast, která umožňuje deklarovat jména uvnitř něj za účelem rozcestníku. Obor názvů poskytuje působnosti (tzv. oboru názvů rozsah) názvy deklarované uvnitř-což jednoduše znamená, že žádné jméno deklarované uvnitř namespace nebude být zaměněny za shodné názvy v dalších oborů.

key insight

název deklarovaný v jmenném prostoru nebude zaměněn za identický název deklarovaný v jiném rozsahu.

v rámci jmenného prostoru musí být všechna jména jedinečná, jinak dojde ke kolizi názvů.

Jmenné prostory se často používají k seskupení souvisejících identifikátorů ve velkém projektu, aby se zajistilo, že se neúmyslně nesrazí s jinými identifikátory. Pokud například vložíte všechny své matematické funkce do jmenného prostoru nazvaného matematika, vaše matematické funkce se nebudou srazit se shodně pojmenovanými funkcemi mimo jmenný prostor matematiky.

promluvíme si o tom, jak vytvořit vlastní jmenné prostory v budoucí lekci.

globální jmenný prostor

V C++, jakékoliv jméno, které není definována uvnitř třídy, funkce, nebo obor názvů je považován za součást implicitně definován obor názvů, nazývá globální obor názvů (někdy se také nazývá globální rozsah).

v příkladu v horní části lekce jsou funkce main () a obě verze myfcn () definovány uvnitř globálního jmenného prostoru. Pojmenování kolize se setkali v příkladu se stane, protože obě verze myFcn() skončit v globální obor názvů, který porušuje pravidlo, že všechny názvy v oboru názvů musí být jedinečné.

std obor názvů

Když C++ byl původně navržen, všech identifikátorů v C++ standard library (včetně std::cin a std::cout) byly k dispozici, aby být použity bez std:: prefix (byly součástí globální obor názvů). To však znamenalo, že jakýkoli identifikátor ve standardní knihovně by mohl být potenciálně v rozporu s jakýmkoli názvem, který jste vybrali pro své vlastní identifikátory (také definovaný v globálním jmenném prostoru). Kód, který pracoval, může mít najednou konflikt pojmenování, když #zahrnete nový soubor ze standardní knihovny. Nebo ještě hůř, programy, které by sestavit podle jedné verze C++ nemusí kompilovat pod budoucí verzi C++, jako nové identifikátory zavedli do standardní knihovny by mohlo mít pojmenování konfliktu s již napsaný kód. Takže C++ přesunul všechny funkce ve standardní knihovně do jmenného prostoru s názvem “ std “ (zkratka pro standard).

Ukazuje se, že jméno std:: cout není ve skutečnosti std:: cout. Je to vlastně jen cout, a std je název jmenného prostoru, který identifikátor cout je součástí. Protože je cout definován ve jmenném prostoru std, název cout nebude v rozporu s žádnými objekty nebo funkcemi pojmenovanými cout, které vytvoříme v globálním jmenném prostoru.

Podobně, při přístupu k identifikátoru, který je definován v oboru názvů (např. std::cout) , musíte říci kompilátoru, že hledáme identifikátor definované uvnitř jmenného prostoru (std).

Key insight

Při použití identifikátor, který je definován uvnitř jmenného prostoru (například std oboru názvů), musíte říci kompilátoru, že identifikátor žije uvnitř jmenného prostoru.

existuje několik různých způsobů, jak to udělat.

explicitní jmenný prostor qualifier std::

nejjednodušší způsob, jak říct kompilátoru, že chceme použít cout ze jmenného prostoru STD, je explicitním použitím předpony std::. Například:

1
2
3
4
5
6
7

#include <iostream>
int main()
{
std::cout << „Hello world!“; // když říkáme, cout, máme na mysli cout definován v std oboru názvů
návrat 0;
}

:: symbol je provozovatel nazývá rozlišení operátor oboru. Identifikátor vlevo od symbolu:: identifikuje jmenný prostor, ve kterém je název vpravo od symbolu:: obsažen. Pokud není nalevo od symbolu :: uveden žádný identifikátor, předpokládá se globální jmenný prostor.

takže když říkáme std:: cout, říkáme „cout, který žije v namespace std“.

toto je nejbezpečnější způsob použití cout, protože neexistuje žádná nejednoznačnost o tom, který cout odkazujeme (ten ve jmenném prostoru std).

Best practice

použijte explicitní předpony jmenného prostoru pro přístup k identifikátorům definovaným v jmenném prostoru.

Pomocí oboru názvů std (a proč se tomu vyhnout)

Další způsob, jak získat přístup identifikátory uvnitř jmenného prostoru, je použít použití směrnice prohlášení. Zde je náš originální program „Hello world“ s using directive:

1
2
3
4
5
6
7
8
9

#include <iostream>
using namespace std; // toto je použití směrnice říká kompilátoru, aby podívejte se na std oboru názvů při řešení identifikátory bez prefix
int main()
{
cout << „Hello world!“; // cout nemá žádný prefix, takže kompilátor bude kontrolovat, jestli cout je definován místně nebo v oboru názvů std
návrat 0;
}

použití směrnice říká, kompilátor zkontrolovat zadaný obor, když se snaží vyřešit identifikátor, který nemá žádný prefix jmenného prostoru. Takže ve výše uvedeném příkladu, když kompilátor určí, jaký identifikátor je cout, zkontroluje jak lokálně (kde je nedefinovaný), tak ve jmenném prostoru std (kde se bude shodovat se STD:: cout).

mnoho textů, výukových programů a dokonce i některých kompilátorů doporučuje nebo používá směrnici using v horní části programu. Používá se však tímto způsobem, je to špatná praxe a je velmi odrazována.

zvažte následující program:

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

#include <iostream> // dovoz prohlášení o std::cout
using namespace std; // je std::cout přístupné jako „cout“
int cout() // prohlašuje, naše vlastní „cout“ funkce
{
návrat 5;
}
int main()
{
cout << “ Ahoj, světe!“; / / Chyba kompilace! Co tady chceme? Ten ve jmenném prostoru std nebo ten, který jsme definovali výše?
návrat 0;
}

výše uvedený program nemusí kompilovat, protože kompilátor teď nemůžu říct, jestli chceme, aby cout funkce, které jsme definovali, nebo cout, který je definován uvnitř std oboru názvů.

při použití směrnice using tímto způsobem může jakýkoli identifikátor, který definujeme, být v rozporu s jakýmkoli identicky pojmenovaným identifikátorem v jmenném prostoru STD. Ještě horší je, že zatímco název identifikátoru dnes nemusí být v rozporu, může být v rozporu s novými identifikátory přidanými do jmenného prostoru std v budoucích revizích jazyka. To byl celý bod přesunutí všech identifikátorů ve standardní knihovně do jmenného prostoru std na prvním místě!

varování

nepoužívejte direktivy (například použití jmenného prostoru std;) v horní části programu. Porušují důvod, proč byly jmenné prostory přidány na prvním místě.

budeme mluvit více o používání prohlášení (a jak je používat zodpovědně) v lekci 6.12-pomocí příkazů .

2.9 — Úvod do preprocesoru

Index

2.7 — Programy s více soubory kódu

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.