Przesłanianie nazw, przestrzenie nazw Bogdan Kreczmer ZPCiR IIAiR PWr pokój 307 budynek C3 bogdan.kreczmer@pwr.wroc.pl Copyright c 2005 2013 Bogdan Kreczmer Niniejszy dokument zawiera materiały do wykładu na temat programowania obiektowego. Jest on udostępniony pod warunkiem wykorzystania wyłacznie do własnych prywatnych potrzeb i może on być kopiowany wyłacznie w całości, razem z niniejsza strona tytułowa.
Nienazwana przestrzeń nazw int Zm = 7; void Funkcja( int Zm ) ::Zm = Zm; void Funkcja( ) int Zm = 55; ::Zm = Zm; Funkcja( 10 ); Funkcja( ); cout << Zm. Globalna: << Zm << endl; cout << Zm. Globalna: << Zm << endl; Wynik działania: Zm. Globalna: 10 Zm. Globalna: 55 W nienazwanej przestrzeni nazw znajduja się wszystkie globalnie definiowane zmienne, typy oraz funkcje. Zmienne te można zawsze odsłonić stosujac nazwę kwalifikowana. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 1
Nienazwana przestrzeń nazw enum Symbole a, b, c; void Funkcja LokalnyTyp( Symbole Param ) enum Symbole x, y, z ; Symbole Zm1 = x; ::Symbole ::Symbole Zm2 = Param; Zm3 = ::a; Symbole x = a; // Tutaj x może być już nazwa zmiennej. Funkcja LokalnyTyp( x ); Sposób odwoływania się do typu z nienazwanej przestrzeni nazw, jak też ich wartości, jest taki sam jak odwoływanie się do zmiennych z tej przestrzeni. W języku C lokalne deklaracje typów nie sa dozwolone (brak przestrzeni nazw). Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 2
Nienazwana przestrzeń nazw class KlasaX //.................................................... public : int PoleA; ; //.................................................................... void Funkcja LokalnaKlasa( ) class KlasaX //................................ public : int PoleB; ; //............................................... ::KlasaX KlasaX ObA; ObB; ObA. PoleA = 1; ObB. PoleB = 9; Klasy, podobnie jak typy wyliczeniowe, możemy zadeklarować lokalnie w danej funkcji. Klasa z nienazwanej przestrzeni nazw jest nadal dostępna. Natomiast lokalna definicja klasy w danej funkcji nie jest widoczna nigdzie indziej poza ta funkcja. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 3
Przestrzeń nazw klasy class Wektor2f //.................................................... public : float x, y; Wektor2f( float x = 0, float y = 0 ); void Zmien( float x, float y ); ; //.................................................................... void Wektor2f::Zmien( float x, float y ) Wektor2f::x = x; Wektor2f::y = y; Wektor2f::Wektor2f( float x, float y ): x( x ), y( y ) Definicja klasy wprowadza jednocześnie przestrzeń nazw zwiazan a z ta klasa. Problem przesłaniania nazw pól przez nazwy parametrów zawsze można ominać stosuja pełna nazwę kwalifikowana tego pola. Nie jest to konieczne w przypadku inicjalizatorów. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 4
Przestrzeń nazw klasy class KlasaZ //......................................................... enum DuzeSymbole A, B, C ; public : enum MaleSymbole a, b, c ; void Metoda( ); ; //........................................................................ void KlasaZ::Metoda( ) MaleSymbole x = a; DuzeSymbole X = A; KlasaZ::MaleSymbole y = KlasaZ::b; KlasaZ::DuzeSymbole Y = KlasaZ::B; // Ten typ jest tutaj niedostępny. Przy definiowaniu typów w klasie obowiazuje taki sam tryb dostępności jak dla pól klasy. O trybie dostępności decyduje sekcja, w której ten typ zostanie zdefiniowany. Typ zdefiniowany w klasie widoczny jest na zewnatrz tylko poprzez pełna nazwę kwalifikowana. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 5
Przestrzeń nazw klasy class KlasaZew //...................................................... public : int PoleA; class KlasaWew //....................................... public : int PoleB; ; //........................................................ ; //........................................................................ KlasaZew KlasaZew::KlasaWew ObZ; ObW; cout << sizeof( int ) << endl; cout << sizeof( KlasaZew ) << endl; cout << sizeof( KlasaZew::KlasaWew ) << endl; Wynik działania: 4 4 4 Definicje klas moga być zagnieżdżane. Nie powoduje to wzrostu rozmiaru obiektu klasy, która zawiera w sobie definicje innych klas, o ile klasy te sa nazwane. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 6
Struktury nienazwane class KlasaZlozone //.................................................. public : int PoleA; union //................................................. int PoleB; int PoleC; ; //......................................................... ; //........................................................................ cout << sizeof( int ) << endl; cout << sizeof( KlasaZlozona ) << endl; Wynik działania: 4 8 Jako strukturę nienazwana w standardzie ISO C++ można zdefiniować jedynie unię. Jednak może ona mieć tylko sekcję publiczna. Struktura danych zdefiniowana przez tego typu unię staje się integralnym składnikiem klasy. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 7
Przestrzeń nazw int ZmA; namespace Zmienne int ZmB = 1; void Podwoj ZmB( ) ZmB = 2; ZmA = 4; Zmienne::ZmB = 2; Zmienne::Podwoj ZmB( ); Przestrzenie nazw umożliwiaja zarzadzanie nazwami zmiennych, funkcji oraz wszystkich definiowanych struktur. Do każdego elementu przestrzeni nazw można zawsze odwołać się poprzez nazwę kwalifikowana. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 8
Przestrzeń nazw int ZmA; namespace Zmienne int ZmB = 1; namespace Zmienne void Podwoj ZmB( ) ZmB = 2; ZmA = 4; Zmienne::ZmB = 2; Zmienne::Podwoj ZmB( ); Definicja przestrzeni nazw może być kontynuowana zarówno w tej samej jednostce translacyjnej, jak też w wielu innych plikach, które wchodzić moga w skład innych jednostek translacyjnych. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 9
Przestrzeń nazw - ważniejsze cechy Definiowanie przestrzeni nazw pozwala rozwiazać konflikty nazw. Definicja przestrzeni nazw może być kontynuowana w kilku jednostek translacyjnych. Wprowadzenie przestrzeni nazw pozwala tworzyć moduły w sensie logicznym, które składaja się z kilku jednostek translacyjnych. W języku C moduł utożsamia się z pojedyncza jednostka translacyjna. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 10
Nienazwana przestrzeń nazw int ZmA; namespace int ZmB = 1; void Podwoj ZmB( ) ZmB = 2; ZmA = 4; ZmB = 2; Podwoj ZmB( ); Odwołanie się do zmiennych, funkcji oraz definicji typów z nienazwanej przestrzeni nazw nie wymaga jawnej kwalifikacji. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 11
Nienazwana przestrzeń nazw program.cpp #include <iostream> int ZmA; static int ZmB = 4; namespace int ZmX = 10; modul.cpp // Tej zmiennej nie można tu zadeklarować // gdyż domyślnie jest ona zmienna extern. // int ZmA; static int ZmB = 40; namespace int ZmX = 100; namespace Zmienne int ZmY = 10; std::cout << ZmX << std::endl; namespace Zmienne // int ZmY = 100; // Deklaracja tej zmiennej spowodowałaby // bład na etapie konsolidacji. SunOS 40> CC prog.cpp modul.cpp SunOS 41>./a.out 10 Każda jednostka translacyjna ma swoja własna unikalna nienazwana przestrzeń nazw. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 12
Przestrzeń nazw int ZmA; namespace Zmienne int ZmA = 1; void Podwoj ZmA( ) ZmA = 2; ZmA = 4; Zmienne::ZmA = 2; Zmienne::Podwoj ZmA( ); cout << ZmA << endl; cout << Zmienne::ZmA << endl; Wynik działania: 4 4 Deklaracje zmiennych lub definicje funkcji i typów w danej przestrzeni nazw przesłaniaja w niej deklaracje i definicje nienazwanej przestrzeni nazw. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 13
Przestrzeń nazw int ZmA; namespace Zmienne int ZmA = 1; void Podwoj ZmA( ) ::ZmA = 2; ZmA = 4; Zmienne::ZmA = 2; Zmienne::Podwoj ZmA( ); cout << ZmA << endl; cout << Zmienne::ZmA << endl; Wynik działania: 8 2 Do każdej przesłoniętej zmiennej, funkcji lub definicji typu zawsze można odwołać podajac jej pełna nazwę kwalifikowana. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 14
Przestrzeń nazw namespace Przyklad enum Symbol a, b ; char ZamienNaZnak( Symbol x ) return x == a? a : b ; Przyklad::Symbol ZmSymb; ZmSymb = Przyklad::a; char Znak = Przyklad::ZamienNaZnak( Przyklad::b ); W przypadku odwoływania się do typów zdefiniowanych w przestrzeni nazw i ich wartości (o ile sa to typy wyliczeniowe) reguły sa analogiczne jak przy odwoływaniu się zmiennych i funkcji z tej przestrzeni. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 15
Przestrzeń nazw - wyszukiwanie Koeniga namespace Przyklad enum Symbol a, b ; char ZamienNaZnak( Symbol x ) return x == a? a : b ; Przyklad::Symbol ZmSymb; ZmSymb = Przyklad::a; char Znak = ZamienNaZnak( Przyklad::b ); // Nazwy tej funkcji nie trzeba uściślać. Reguła wyszukiwania Koeniga (ang. Koenig lookup): Jeżeli co najmniej jeden typ argumentu danej funkcji zostanie zdefiniowany w tej samej przestrzeni nazw, to nazwy funkcji nie trzeba uściślać. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 16
Przestrzeń nazw - dyrektywa using namespace Zmienne int ZmA = 0; int Zm1 = Zmienne::ZmA; using namespace Zmienne; Zm1 = ZmA; Dyrektywa using powoduje przeniesienie wszystkich nazw z danej przestrzeni do przestrzeni nazw, w której zostanie użyta. W demonstrowanym przykładzie jest to przestrzeń nienazwana. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 17
Przestrzeń nazw - dyrektywa using namespace Zmienne int ZmA = 0; int Zm1 = Zmienne::ZmA; int ZmA = 5; using namespace Zmienne; Zm1 = ZmA; Zm1 = ::ZmA; Zm1 = Zmienne::ZmA; // Tutaj pojawia się niejednoznaczność. Przeniesienie z jednej przestrzeni nazw do innej może prowadzić do konfliktu nazw. Sa one sygnalizowane na etapie kompilacji. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 18
Przestrzeń nazw - dyrektywa using namespace Zmienne int ZmA = 0; int Zm1 = Zmienne::ZmA; Zm1 = Zmienne::ZmA; using namespace Zmienne; Zm1 = ZmA; Zm1 = Zmienne::ZmA; Przeniesienie nazw z danej przestrzeni można zawęzić do pojedynczego bloku. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 19
Przestrzeń nazw - deklaracja using namespace Zmienne int ZmA = 0; int ZmB = 5; int Zm1 = Zmienne::ZmA; using Zmienne::ZmA; Zm1 = ZmA; Zm1 = Zmienne::ZmB; using Zmienne::ZmB; Zm1 = ZmB; Zm1 = ZmB; Stosujac deklarację using można przenieść tylko jedna wybrana nazwę do danej przestrzeni. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 20
Przestrzeń nazw - zagnieżdżanie namespace Zmienne int ZmA = 0; namespace Inne int ZmB = 5; Zmienne::ZmA = Zmienne::Inne::ZmB; using namespace Zmienne; ZmA = Inne::ZmB; using namespace Inne; ZmA = ZmB; // Może również użyć nazwy Zmienne::Inne Przestrzenie nazw moga być zagnieżdżane. Można je też kolejno przenosić do wspólnej przestrzeni, o ile nie spowoduje to konfliktu nazw. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 21
Przestrzeń nazw - zagnieżdżanie namespace Zmienne int ZmA = 0; namespace Inne int ZmB = 5; Zmienne::ZmA = Zmienne::Inne::ZmB; using namespace Zmienne::Inne; Zmienne::ZmA = ZmB; using namespace Zmienne; ZmA = ZmB; Przestrzenie nazw można zawsze przenieść do wspólnej przestrzeni w dowolnej kolejności. Jest to możliwe pod warunkiem, że nie ma konfliktu nazw we wspólnej przestrzeni. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 22
Przestrzeń nazw - zagnieżdżanie namespace Zmienne int ZmA = 0; namespace Inne int ZmB = 5; int ZmC = 2 int ZmX = Inne::ZmB; using Inne::ZmB; int ZmY = ZmB + Inne::ZmC; using namespace Inne; int ZmZ = ZmB - ZmC; Dyrektywę oraz deklarację using można również używać w definicji dowolnej przestrzeni nazw. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 23
Dyrektywa wersus deklaracja #include <iostream> #include <cmath> #include <iostream> #include <cmath> using namespace std; #include <iostream> #include <cmath> using std::cout; using std::endl; using std::sqrt; std::cout << Witaj << std::endl; cout << Witaj << endl; cout << Witaj << endl; std::cout << std::sqrt(2) << std::endl; cout << sqrt(2) << endl; cout << sqrt(2) << endl; Użycie dyrektywy jest wygodne. Jednak powoduje ono zaśmiecenie aktualnej przestrzeni nazw wszystkimi nazwami z danego modułu. Z tego powodu jeżeli wykorzystywane sa tylko niektóre elementy z danego modułu, zalecane jest korzystanie z deklaracji using. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 24
Podsumowanie Definiowanie przestrzeni nazw pozwala rozwiazać konflikty nazw. Tym samym daje znacznie większa swobodę programiście. Definicja przestrzeni nazw może być kontynuowana w kilku jednostek translacyjnych. Jedna z ważniejszych konsekwencji wykorzystywania przestrzeni nazw jest możliwość tworzenia modułów w sensie logicznym, a nie tak jak to ma miejsce, np. w języku C tylko w sensie stricte fizycznym. Dyrektywa using powinna być wykorzystywana możliwie oszczędnie. Zamiast niej zalecane jest wykorzystywanie deklaracji using. Zapewnia to przeniesienie do danej przestrzeni nazw tylko wybranych elementów. Tym samym zapewnia lepsza kontrolę nad nazwami. Dyrektywy using nie należy używać w plikach nagłówkowych. Jej użycie tam jest przejawem bardzo złego stylu programowania. W plikach nagłówkowych należy również unikać używania deklaracji using. Jednak to jest już znacznie mniej niebezpieczne niż użycie dyrektywy. Copyright c 2005 2013 Bogdan Kreczmer Przesłanianie nazw, przestrzenie nazw 25