r18-06.doc

22
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ć wzorce 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

Upload: greg

Post on 14-Sep-2015

212 views

Category:

Documents


0 download

TRANSCRIPT

Szablon dla tlumaczy

2

Cz I ( Podstawy obsugi systemu WhizBang (Nagwek strony)

Rozdzia 18.Przestrzenie nazw

Przestrzenie nazw pomagaj programistom unika konfliktw nazw powstajcych w trakcie korzystania z wicej ni jednej biblioteki.

Z tego rozdziau dowiesz si

jak funkcje i klasy s rozpoznawane poprzez nazwy,

jak stworzy przestrze nazw,

jak uywa przestrzeni nazw,

jak uywa standardowej przestrzeni nazw std.

Zaczynamy

Konflikty nazw s rdem irytacji zarwno dla programistw C, jak i C++. Konflikt nazw powstaje wtedy, gdy w dwch czciach programu, w tym samym zakresie, wystpuje kilka takich samych nazw. Najczstsz przyczyn konfliktu jest wystpienie tej samej nazwy w kilku rnych bibliotekach. Na przykad, biblioteka klasy kontenera prawie na pewno bdzie deklarowa i implementowa klas List (lista). Wicej na temat klas kontenerw dowiesz si z rozdziau 19., w ktrym bdziemy omawia szablony.

Nie powinno nas dziwi take wystpowanie klasy List w bibliotece zwizanej z okienkami. Przypumy teraz, e chcemy stworzy list okien wystpujcych w naszej aplikacji. Zakadamy take, e uywamy klasy List z biblioteki klasy kontenera. Deklarujemy egzemplarz klasy List z biblioteki okienkowej (w celu przechowania okien) i okazuje si, e funkcja skadowa, ktr wywoujemy, jest niedostpna. Kompilator dopasowa deklaracj naszej klasy List do kontenera List w bibliotece standardowej, mimo i w rzeczywistoci chcielimy skorzysta z klasy List w bibliotece okienkowej, stworzonej przez kogo innego.

Przestrzenie nazw su do podzielenia globalnej przestrzeni nazw i wyeliminowania (lub przynajmniej ograniczenia) konfliktw nazw. Przestrzenie nazw s nieco podobne do klas i posiadaj bardzo podobn skadni.

Elementy zadeklarowane w przestrzeni nazw nale do tej przestrzeni. Wszystkie elementy w przestrzeni nazw s widoczne publicznie. Przestrzenie nazw mog by zagniedone. Funkcje mog by definiowane w ciele przestrzeni nazw lub poza nim. Jeli funkcja jest zdefiniowana poza ciaem przestrzeni nazw, musi by kwalifikowana nazw tej przestrzeni.

Funkcje i klasy s rozpoznawane poprzez nazwy

Podczas przetwarzania kodu rdowego i budowania listy nazw funkcji i zmiennych, kompilator sprawdza take wystpowanie konfliktw nazw. Te konflikty, ktrych kompilator nie potrafi rozwiza sam, mog by czasem rozwizane przez program czcy (linker).

Kompilator nie potrafi wykry konfliktu nazw pomidzy jednostkami kompilacji (na przykad pomidzy plikami object); jest to zadaniem programu czcego (linkera). Dlatego kompilator nie zgosi nawet ostrzeenia.

Nie powinien nas dziwi komunikat linkera informujcy o powielonym identyfikatorze, bdcym jakim nazwanym typem. Ten komunikat pojawia si, gdy zdefiniujemy t sam nazw w tym samym zakresie w rnych jednostkach kompilacji. Jeli powielimy nazw w tym samym zakresie w tym samym pojedynczym pliku, pojawi si bd kompilacji. Poniszy kod powoduje wystpienie bdu 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

Mj linker zgasza komunikat: in second.obj: integerValue already defined in first.obj (w second.obj: integerValue ju jest zdefiniowane w first.obj). Gdyby te nazwy wystpoway w innych zakresach, kompilator i linker przestayby zgasza bdy.

Istnieje take moliwo, e kompilator zgosi ostrzeenie ukrywaniu identyfikatora. Kompilator powinien ostrzec, w pliku first.cpp, e integerValue w funkcji main() ukrywa zmienn globaln o tej samej nazwie.

Aby uy zmiennej integerValue zadeklarowanej poza funkcj main(), musisz jawnie przypisa t zmienn do zakresu globalnego. Spjrzmy na poniszy przykad, w ktrym warto 10 przypisujemy do zmiennej integerValue poza main(), a nie do zmiennej integerValue zadeklarowanej wewntrz 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

UWAGAZwr uwag na uycie operatora zakresu (::) wskazujcego, 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 bd linkera.

NOWE OKRELENIE

Okrelenie widoczno jest uywane do oznaczenia zakresu zdefiniowanego obiektu, bez wzgldu na to, czy jest to zmienna, klasa, czy funkcja. Na przykad, zmienna zadeklarowana i zdefiniowana poza funkcj posiada zakres pliku, czyli globalny. Zmienna ta jest widoczna od punktu jej zadeklarowania a do koca pliku. Zmienna o zakresie bloku, czyli lokalna, wystpuje wewntrz bloku kodu. Najczstszymi przykadami takich zmiennych s zmienne zadeklarowane wewntrz funkcji. Zakres zmiennych przedstawia poniszy przykad:

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 wewntrz funkcji f() oraz main(). Nastpna definicja znajduje si wewntrz funkcji f() i ma nazw localScopeInt. Ta zmienna ma zakres lokalny, co oznacza, e jest widoczna tylko w bloku, w ktrym zostaa zdefiniowana.

Funkcja main() nie moe odwoywa si do zmiennej localScopeInt zdefiniowanej wewntrz funkcji f(). Gdy funkcja f() koczy dziaanie, 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 nastpne definicje, anotherLocal oraz localScopeInt, maj zakres blokowy. Gdy dochodzimy do nawiasu klamrowego zamykajcego, zmienne te staj si niewidoczne.

Zauwa, e zmienna localScopeInt ukrywa wewntrz bloku zmienn localScopeInt zdefiniowan tu przed nawiasem klamrowym otwierajcym blok (drug zmienn localScopeInt zdefiniowan w programie). Gdy program przechodzi poza nawias klamrowy zamykajcy blok, druga zdefiniowana zmienna localScopeInt znw staje si widoczna. Wszelkie zmiany dokonane w zmiennej localScopeInt zdefiniowanej wewntrz bloku nie maj wpywu na zawarto innych zmiennych localScopeInt.

NOWE OKRELENIE

Nazwy mog by czone wewntrznie i zewntrznie. Te dwa terminy stosujemy okrelajc dostpno nazwy w rnych jednostkach kompilacji lub wewntrz pojedynczej jednostki kompilacji. Nazwa czona wewntrznie moe by uywana tylko w tej jednostce kompilacji, w ktrej jest zdefiniowana. Na przykad, zmienna zdefiniowana jako czona wewntrznie moe by wykorzystywana przez funkcje w tej samej jednostce kompilacji. Nazwy czone zewntrznie s dostpne take w innych jednostkach kompilacji. Poniszy przykad demonstruje czenie wewntrzne i zewntrzne:

// 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 zewntrznie (ang. external). Cho jest zdefiniowana w pliku first.cpp, moe z niej korzysta take plik second.cpp. Dwie zmienne j, wystpujce w obu plikach, s zmiennymi const, wic domylnie s czone wewntrznie. Moemy przesoni domylne czenie dla const, stosujc jawn deklaracj, tak jak ta:

// plik first.cp

extern const int j = 10;

// plik second.cpp

extern const int j;

#include

int main()

{

std::cout 0 )

if( y < MAX_SCREEN_Y && y > 0 )

platform.resize( x, y ); // specyficzna funkcja

}

// . . . dalsze definicje

}

Wida tu, jak szybko zamieca si przestrze nazw! Ten przykad skada si z okoo dwudziestu linii; wyobra sobie, jak by wygldaa cztery razy wiksza przestrze nazw.

Definiowanie funkcji poza przestrzeni nazw

Funkcje danej przestrzeni nazw powinny by definiowane poza ciaem tej przestrzeni. W ten sposb jawnie separujemy deklaracje funkcji od ich definicji a take utrzymujemy w porzdku przestrze nazw. Oddzielenie definicji funkcji od przestrzeni nazw umoliwia take umieszczenie przestrzeni i zawartych w niej deklaracji w pliku nagwkowym; definicje mog zosta umieszczone w pliku implementacji.

Na przykad:

// plik header.h

namespace Window {

void move( int x, int y );

// . . . inne deklaracje

}

// plik impl.cpp

void Window::move( int x, int y )

{

// kod do przesuwania okna

}

Dodawanie nowych skadowych

Nowe skadowe mog zosta dodane do przestrzeni nazw tylko w jej ciele. Nie mona definiowa nowych skadowych, uywajc tylko kwalifikatorw. Jedyne, czego mona oczekiwa od tego rodzaju definicji, to bd kompilacji. Demonstruje to poniszy przykad:

namespace Window {

// mnstwo deklaracji

}

// ... jaki kod

int Window::newIntegerNamespace; // przykro mi, nie mona tego zrobi

Powysza linia kodu jest niedozwolona. Twj kompilator wypisze komunikat zgaszajcy ten bd. Aby go poprawi czyli unikn przenie deklaracj do ciaa przestrzeni nazw.

Wszystkie skadowe zawarte w przestrzeni nazw s publiczne. Poniszy kod nie skompiluje si:

namespace Window {

private:

void move( int x, int y );

}

Zagniedanie przestrzeni nazw

Przestrzenie nazw mog by zagniedane w innych przestrzeniach nazw. Jest to moliwe, gdy definicja przestrzeni nazw jest take deklaracj. Tak jak w przypadku wszystkich innych przestrzeni nazw, naley wtedy kwalifikowa nazw zagniedonej przestrzeni za pomoc nazwy przestrzeni zewntrznej. Gdy mamy kilka kolejno zagniedonych przestrzeni, musimy kwalifikowa kolejno kad z nich. Na przykad, poniszy kod przedstawia nazwan przestrze nazw, zagniedon w innej nazwanej przestrzeni nazw:

namespace Window {

namespace Pane {

void size( int x, int y );

}

}

Aby odwoa si do funkcji size() spoza przestrzeni nazw Window, musimy kwalifikowa funkcj nazwami obu przestrzeni nazw. Demonstruje to poniszy kod:

int main()

{

Window::Pane::size( 10, 20 );

return 0;

}

Uywanie przestrzeni nazw

Przyjrzyjmy si przykadowi uycia przestrzeni nazw i wynikajcemu z niego przykadowi uycia operatora zakresu. Najpierw deklaruj wszystkie typy i funkcje uywane w przestrzeni nazw Window. Po zdefiniowaniu wszystkiego, co jest wymagane, definiuj zadeklarowane funkcje skadowe. Te funkcje skadowe s definiowane poza przestrzeni nazw; nazwy s jawnie identyfikowane za pomoc operatora zakresu. Uycie przestrzeni nazw demonstruje listing 18.1.

Listing 18.1. Uycie przestrzeni nazw

0: #include

1: // Uycie przestrzeni nazw

2:

3: namespace Window

4: {

5: const int MAX_X = 30 ;

6: const int MAX_Y = 40 ;

7: class Pane

8: {

9: public:

10: Pane() ;

11: ~Pane() ;

12: void size( int x, int y ) ;

13: void move( int x, int y ) ;

14: void show( ) ;

15: private:

16: static int cnt

17: int x ;

18: int y ;

19: } ;

20: }

21:

22: int Window::Pane::cnt = 0 ;

23: Window::Pane::Pane() : x(0), y(0) { }

24: Window::Pane::~Pane() { }

25:

26: void Window::Pane::size( int x, int y )

27: {

28: if( x < Window::MAX_X && x > 0 )

29: Pane::x = x ;

30: if( y < Window::MAX_Y && y > 0 )

31: Pane::y = y ;

32: }

33: void Window::Pane::move( int x, int y )

34: {

35: if( x < Window::MAX_X && x > 0 )

36: Pane::x = x ;

37: if( y < Window::MAX_Y && y > 0 )

38: Pane::y = y ;

39: }

40: void Window::Pane::show( )

41: {

42: std::cout