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
są
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.
Plik z chomika:
Wojteczek
Inne pliki z tego folderu:
rdodc.pdf
(84 KB)
rdodb.pdf
(72 KB)
rdoda.pdf
(184 KB)
r21.pdf
(370 KB)
r20.pdf
(275 KB)
Inne foldery tego chomika:
Cisco
Kurs C++ od zera do hackera v. 1.0
Pascal
ZAHASŁOWANE
Zgłoś jeśli
naruszono regulamin