r18.pdf

(248 KB) Pobierz
Szablon dla tlumaczy
Rozdział 18.
Przestrzenie nazw
Przestrzenie nazw pomagają programistom unikać konfliktów nazw powstających w trakcie
korzystania z więcej niż jednej biblioteki.
Z tego rozdziału dowiesz się
jak funkcje i klasy są rozpoznawane poprzez nazwy,
jak stworzyć przestrzeń nazw,
jak używać przestrzeni nazw,
jak używać standardowej przestrzeni nazw std .
Zaczynamy
Konflikty nazw są źródłem irytacji zarówno dla programistów C, jak i C++. Konflikt nazw
powstaje wtedy, gdy w dwóch częściach programu, w tym samym zakresie, występuje kilka takich
samych nazw. Najczęstszą przyczyną konfliktu jest wystąpienie tej samej nazwy w kilku różnych
bibliotekach. Na przykład, biblioteka klasy kontenera prawie na pewno będzie deklarować i
implementować klasę List (lista). Więcej na temat klas kontenerów dowiesz się z rozdziału 19.,
w którym będziemy omawiać szablony.
Nie powinno nas dziwić także występowanie klasy List w bibliotece związanej z okienkami.
Przypuśćmy teraz, że chcemy stworzyć listę okien występujących w naszej aplikacji. Zakładamy
także, że używamy klasy List z biblioteki klasy kontenera. Deklarujemy egzemplarz klasy List
z biblioteki okienkowej (w celu przechowania okien) i okazuje się, że funkcja składowa, którą
wywołujemy, jest niedostępna. Kompilator dopasował deklarację naszej klasy List do kontenera
List w bibliotece standardowej, mimo iż w rzeczywistości chcieliśmy skorzystać z klasy List w
bibliotece okienkowej, stworzonej przez kogoś innego.
Przestrzenie nazw służą do podzielenia globalnej przestrzeni nazw i wyeliminowania (lub
przynajmniej ograniczenia) konfliktów nazw. Przestrzenie nazw są nieco podobne do klas i
posiadają bardzo podobną składnię.
Elementy zadeklarowane w przestrzeni nazw należą do tej przestrzeni. Wszystkie elementy w
przestrzeni nazw są widoczne publicznie. Przestrzenie nazw mogą być zagnieżdżone. Funkcje
mogą być definiowane w ciele przestrzeni nazw lub poza nim. Jeśli funkcja jest zdefiniowana poza
ciałem przestrzeni nazw, musi być kwalifikowana nazwą tej przestrzeni.
Funkcje i klasy są rozpoznawane poprzez
nazwy
Podczas przetwarzania kodu źródłowego i budowania listy nazw funkcji i zmiennych, kompilator
sprawdza także występowanie konfliktów nazw. Te konflikty, których kompilator nie potrafi
rozwiązać sam, mogą być czasem rozwiązane przez program łączący (linker).
Kompilator nie potrafi wykryć konfliktu nazw pomiędzy jednostkami kompilacji (na przykład
pomiędzy plikami object ); jest to zadaniem programu łączącego (linkera). Dlatego kompilator
nie zgłosi nawet ostrzeżenia.
Nie powinien nas dziwić komunikat linkera informujący o powielonym identyfikatorze, będącym
jakimś nazwanym typem. Ten komunikat pojawia się, gdy zdefiniujemy tę samą nazwę w tym
samym zakresie w różnych jednostkach kompilacji. Jeśli powielimy nazwę w tym samym zakresie
w tym samym pojedynczym pliku, pojawi się błąd kompilacji. Poniższy kod powoduje
wystąpienie błędu łączenia podczas kompilowania i łączenia:
// plik first.cpp
int integerValue = 0;
int main( ) {
int integerValue = 0;
// . . .
return 0;
};
// plik second.cpp
int integerValue = 0;
// koniec pliku second.cpp
Mój linker zgłasza komunikat: in second.obj: integerValue already defined in
first.obj (w second.obj : integerValue już jest zdefiniowane w first.obj ). Gdyby te nazwy
występowały w innych zakresach, kompilator i linker przestałyby zgłaszać błędy.
Istnieje także możliwość, że kompilator zgłosi ostrzeżenie ukrywaniu identyfikatora . Kompilator
powinien ostrzec, w pliku first.cpp , że integerValue w funkcji main() ukrywa zmienną
globalną o tej samej nazwie.
Aby użyć zmiennej integerValue zadeklarowanej poza funkcją main() , musisz jawnie
przypisać tę zmienną do zakresu globalnego. Spójrzmy na poniższy przykład, w którym wartość
10 przypisujemy do zmiennej integerValue poza main() , a nie do zmiennej integerValue
zadeklarowanej wewnątrz main() :
// plik first.cpp
int integerValue = 0;
int main()
{
int integerValue = 0;
::integerValue = 10; // przypisanie do globalnej zmiennej
// . . .
return 0;
};
// plik second.cpp
int integerValue = 0;
// koniec pliku second.cpp
UWAGA Zwróć uwagę na użycie operatora zakresu ( :: ) wskazującego, że chodzi o zmienną
globalną integerValue , a nie lokalną.
Problem z dwiema zmiennymi globalnymi zdefiniowanymi poza funkcjami polega na tym, że
posiadają one takie same nazwy i tę samą widoczność, a tym samym powodują błąd linkera.
NOWE OKREŚLENIE
Określenie widoczność jest używane do oznaczenia zakresu zdefiniowanego obiektu, bez względu
na to, czy jest to zmienna, klasa, czy funkcja. Na przykład, zmienna zadeklarowana i zdefiniowana
poza funkcją posiada zakres pliku , czyli globalny . Zmienna ta jest widoczna od punktu jej
zadeklarowania aż do końca pliku. Zmienna o zakresie bloku , czyli lokalna , występuje wewnątrz
bloku kodu. Najczęstszymi przykładami takich zmiennych są zmienne zadeklarowane wewnątrz
funkcji. Zakres zmiennych przedstawia poniższy przykład:
int globalScopeInt = 5;
void f()
{
int localScopeInt = 10;
}
int main()
{
int localScopeInt = 15;
{
int anotherLocal = 20;
int localScopeInt = 30;
}
return 0;
}
Pierwsza definicja int , zmienna globalScopeInt , jest widoczna wewnątrz funkcji f() oraz
main() . Następna definicja znajduje się wewnątrz funkcji f() i ma nazwę localScopeInt . Ta
zmienna ma zakres lokalny, co oznacza, że jest widoczna tylko w bloku, w którym została
zdefiniowana.
Funkcja main() nie może odwoływać się do zmiennej localScopeInt zdefiniowanej wewnątrz
funkcji f() . Gdy funkcja f() kończy działanie, zmienna ta wychodzi poza zakres. Zmienna ta ma
zakres blokowy.
Zwróć uwagę, że zmienna localScopeInt w funkcji main() nie koliduje ze zmienną
localScopeInt w funkcji f() . Dwie następne definicje, anotherLocal oraz localScopeInt ,
mają zakres blokowy. Gdy dochodzimy do nawiasu klamrowego zamykającego, zmienne te stają
się niewidoczne.
Zauważ, że zmienna localScopeInt ukrywa wewnątrz bloku zmienną localScopeInt
zdefiniowaną tuż przed nawiasem klamrowym otwierającym blok (drugą zmienną
localScopeInt zdefiniowaną w programie). Gdy program przechodzi poza nawias klamrowy
zamykający blok, druga zdefiniowana zmienna localScopeInt znów staje się widoczna.
Wszelkie zmiany dokonane w zmiennej localScopeInt zdefiniowanej wewnątrz bloku nie mają
wpływu na zawartość innych zmiennych localScopeInt .
NOWE OKREŚLENIE
Nazwy mogą być łączone wewnętrznie i zewnętrznie . Te dwa terminy stosujemy określając
dostępność nazwy w różnych jednostkach kompilacji lub wewnątrz pojedynczej jednostki
kompilacji. Nazwa łączona wewnętrznie może być używana tylko w tej jednostce kompilacji, w
której jest zdefiniowana. Na przykład, zmienna zdefiniowana jako łączona wewnętrznie może być
wykorzystywana przez funkcje w tej samej jednostce kompilacji. Nazwy łączone zewnętrznie
dostępne także w innych jednostkach kompilacji. Poniższy przykład demonstruje łączenie
wewnętrzne i zewnętrzne:
// plik first.cpp
int externalInt = 5;
const int j = 10;
int main()
{
return 0;
}
// plik second.cpp
extern int externalInt;
int anExternalInt = 10;
const int j = 10;
Zmienna externalInt zdefiniowana w pliku first.cpp jest łączona zewnętrznie (ang. external ).
Choć jest zdefiniowana w pliku first.cpp , może z niej korzystać także plik second.cpp . Dwie
zmienne j , występujące w obu plikach, są zmiennymi const , więc domyślnie są łączone
wewnętrznie. Możemy przesłonić domyślne łączenie dla const , stosując jawną deklarację, taką
jak ta:
// plik first.cp
extern const int j = 10;
// plik second.cpp
extern const int j;
#include <iostream>
int main()
{
std::cout << "j ma wartosc " << j << std::endl;
return 0;
}
Zwróć uwagę, że wywołujemy cout z określeniem przestrzeni nazw std ; w ten sposób możemy
korzystać ze standardowej biblioteki ANSI. Po zbudowaniu i uruchomieniu, program ten
wypisuje:
j ma wartosc 10
Komitet standaryzacji potępił przedstawione poniżej zastosowanie:
static int staticInt = 10;
int main()
{
// ...
}
Użycie modyfikatora static w celu ograniczenia zakresu zmiennych zewnętrznych nie jest już
zalecane, a w przyszłości może stać się niedozwolone. Zamiast static powinieneś używać
przestrzeni nazw.
TAK
NIE
Zamiast statc używaj przestrzeni nazw.
Nie używaj słowa kluczowego static w
zmiennych zdefiniowanych w zakresie pliku.
2971187.001.png
Zgłoś jeśli naruszono regulamin