Obiekty i metody stałe Bogdan Kreczmer ZPCiR IIAiR PWr pokój 307 budynek C3 bogdan.kreczmer@pwr.wroc.pl Copyright c 2005 2008 Bogdan Kreczmer Niniejszy dokument zawiera materiały do wykładu na temat programowania obiektowego. Jest on udostępiony 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.
Definiowanie stałych i ich znaczenie const int StalaInt = 5; int ZmienInt = 4; char Tablica1[StalaInt]; char Tablica2[ZmienInt]; // Dopuszczalna w ISO/ANSI C++ ale nie w ANSI C // Niedopuszczalne w ISO/ANSI C++ i ANSI C int main() StalaInt = ZmienInt; // To podstawienie jest niedopuszczalne. ZmienInt = StalaInt; return 0; Obiekty stałe i metody stałe 1
Wskaźniki i modyfikator const char const wsk; (const char ) 0000000 1111111 0000000 1111111 0000000 1111111 0000000 1111111 0000000 1111111 w l ó d k a 0000000000000000000000 1111111111111111111111 1111111111111111111111 0000000000000000000000 0000000000000000000000 1111111111111111111111 1111111111111111111111 0000000000000000000000 1111111111111111111111 0000000000000000000000 1111111111111111111111 0000000000000000000000 char const wsk; 0000000000000 1111111111111 1111111111111 0000000000000 1111111111111 0000000000000 1111111111111 0000000000000 1111111111111 0000000000000 w l ó d k a Obiekty stałe i metody stałe 2
Definiowanie stałych i ich znaczenie const char NapisStaly = "Jakiś łańcuch"; char const WskStaly = Taka inicjalizacja jest ryzykowna ; char WskNapisu = Równie zła inicjalizacja jak powyżej ; int main() WskStaly = "Inny łańcuch"; // To podstawienie jest niedopuszczalne. WskStaly[1] =! ; // Tutaj powinna nastapić katastrofa. NapisStaly[3] = $ ; NapisStaly = "Inny łańcuch"; // Ta operacja jest niedopuszczalna NapisStaly = WskStaly; WskStaly = NapisStaly; // Ta operacja jest niedopuszczalna. Obiekty stałe i metody stałe 3
Definiowanie parametrów stałych char Kopiuj( char Cel, const char Zrodlo ) /* */ Zrodlo[2] = x ; /* Tego na pewno być nie może */... Zrodlo = Zrodlo + 1; /* Takie operacje dopuszczamy */ return Zrodlo; /* To jest również niemożliwe */ return Cel; /* Ten wskaźnik możemy przekazać */ /* */ int main() char Tablica[30]; Kopiuj( Tablica, łódka );... Kopiuj( Tablica, łódka )[0] = w ;... Obiekty stałe i metody stałe 4
Obiekty stałe class LiczbaZespolona public: float re, im; ; int main() LiczbaZespolona ZmienZesp; LiczbaZespolona const StalaZesp = ZmienZesp; // Można zainicjalizować float re = StalaZesp. re; float im = StalaZesp. im; // Możemy odczytać wartości // obiektu stałego StalaZesp. re = re; StalaZesp. im = im;... // Ta operacja jest niedozwolona // Ta również Obiekty stałe i metody stałe 5
Obiekty stałe class LiczbaZespolona //......................................................... public: float re, im; ; //.................................................................................. int main() LiczbaZespolona const StalaZesp; StalaZesp. re = 20; ((LiczbaZespolona&)StalaZesp). im = 20; const cast<liczbazespolona&>(stalazesp). im = 20;... // Ta operacja jest niedozwolona // Ta już jest poprawna // A to bardziej poprawne Zmianę wartości obiektu stałego można wymusić poprzez rzutowanie. Nie jest to jednak dobry pomysł. Obiekty stałe i metody stałe 6
Obiekty stałe class LiczbaZespolona // float re, im; public: float Re() const return re; // Deklaracja metody stałej float Im() return im; ; // int main() LiczbaZespolona LiczbaZespolona const ZmienZesp; StalaZesp = ZmienZesp; float re = StalaZesp.Re(); float im = StalaZesp.Im(); // Tej metody nie możemy użyć re = ZmienZesp.Re(); im = ZmienZesp.Im();... // Tu jest wszystko dobrze Obiekty stałe i metody stałe 7
Metody stałe class LiczbaZespolona // float re, im; public: float Re() const return re; float Im() const return im; void Zmien(float r, float i) const re = r; im = i; // BŁAD double Modul() const; ; // double LiczbaZespolona::Modul() const return sqrt( re re + im im); W metodach stałych nie można dokonywać zmian wartości pól obiektu. Można jedynie odczytywać te wartości. Obiekty stałe i metody stałe 8
Metody stałe class LiczbaZespolona // float re, im; public: float Re() const return re; float Im() const return im; double Modul() const; ; // double LiczbaZespolona::Modul() const return sqrt(re() Re() + Im() Im()); W metodach stałych można odwoływać się do innych metod stałych. Obiekty stałe i metody stałe 9
Metody stałe class LiczbaZespolona // float re, im; public: float Re() const return re; float Im() return im; double Modul() const; ; // double LiczbaZespolona::Modul() const return sqrt(re() Re() + Im() Im()); // BŁAD W metodach stałych nie można odwoływać się do metod, które nie sa metodami stałymi. Obiekty stałe i metody stałe 10
Metody stałe class LiczbaZespolona // float re, im; public: float Re() const return re; float Im() const return im; void Zmien(float r, float i) const; ; // // Czy ta metoda jest faktycznie stała? // Dzięki rzutowaniu otrzy- // mujemy obiekt zmienny. void LiczbaZespolona::Zmien(float r, float i) const const cast<liczbazespolona&>( this ). re = r; const cast<liczbazespolona&>( this ). im = i; Brak zezwolenia na zmianę stanu obiektu w metodach stałych zawsze można ominać wykorzystujac rzutowanie. Jednak jest to BARDZO NIEDOBRY pomysł. Obiekty stałe i metody stałe 11
Metody stałe class TablicaZeWskaznikiem // int Tablica[ROZMIAR STOSU]; int WskElem; public: void ZmienWTablicy() const Tablica[0] = 4; void ZmienPrzezWsk() const WskElem = 4; TablicaZeWskaznikiem() WskElem = Tablica; ; // // Ta metoda nie może być stała int main() TablicaZeWskaznikiem const StStaly; StStaly.ZmienPrzezWsk();... // A jednak nastapiła zmiana!!! Dla obiektów stałych operacje zapisu sa kontrolowane jedynie na etapie kompilacji. Obiekty stałe i metody stałe 12
Deklaracja obiektów stałych class KlasaPusta ; public : const int IntStala; const KlasaPusta ObPustyStaly; Zarówno zwykłe zmienne jak też obiekty, które sa deklarowane jako stałe, musza być w momencie deklaracji zainicjalizowane. Jest to wymóg formalny i nie ma tu znaczenia zawartość obiektu. Konieczność wykonania tej operacji wynika z faktu, że później z założenia ich zawartość jest już niezmienna. Choć możliwe jest obejście tego ograniczenia, to jednak ma ono charakter nieformalny. Obiekty stałe i metody stałe 13
Deklaracja obiektów stałych class KlasaPusta ; public : int IntModyf; const int IntStala = IntModyf; KlasaPusta ObPustyModyf; const KlasaPusta ObPustyStaly = ObPustyModyf; Inicjalizację można dokonać pośrednio przepisujac zawartość innej zmiennej lub obiektu stałego lub też modyfikowalnego. Obiekty stałe i metody stałe 14
Deklaracja obiektów stałych class KlasaPusta public : ; KlasaPusta( ) const int IntStala = 27; const KlasaPusta ObPustyStaly; Inicjalizację zmiennej zadeklarowanej jako stała można dokonać poprzez przypisanie wartości stałej w momencie jej deklaracji. W przypadku obiektu za inicjalizację odpowiedzialny jest konstruktor. Obiekty stałe i metody stałe 15
Deklaracja pól stałych class KlasaZPoleStalym public : const int PoleStale; KlasaZPolemStalym( ): PoleStale(27) KlasaZPolemStalym( float ): PoleStale(102) ; KlasaZPolemStalym( int ID ): PoleStale(ID) W przypadku deklaracji pola stałego, jego inicjalizacja musi być zrealizowana w liście inicjalizacyjnej konstruktora. Operacja ta musi być wykonana w liście inicjalizacyjnej każdego konstruktora danej klasy. Jednak sposób inicjalizacji nie musi być jednakowy. Mechanizm ten może być wykorzystany do rozpoznania konstruktora, który został wykorzystany przy tworzeniu danego obiektu. Obiekty stałe i metody stałe 16
Pola statyczne class KlasaZPolemStatycznym public : static int PoleStatyczne; ; int main() const KlasaZPolemStatycznym Ob; Ob. PoleStatyczne = 5; Pola statyczne nie sa częścia obiektu. Stanowia one raczej część klasy. Z tego powodu pomimo, że obiekt został zadeklarowany jako stały, pole to może zostać zmodyfikowane. Obiekty stałe i metody stałe 17
Pola statyczne class KlasaZPolemStatycznym public : static int PoleStatyczne; ; static int Wez( ) const return PoleStatyczne; static void Zmien(int Wartosc ) PoleStatyczne = Wartosc; int main() const KlasaZPolemStatycznym Ob; Ob.Zmien(5); Modyfikator const przy deklaracji obiektu jako obiektu stałego nie dotyczy pól statycznych, tym samym nie może on dotyczyć również metod statycznych. Można więc posługujac się obiektem stałym wywoływać metodę statyczna, która nie jest metoda stała. Dalsza konsekwencja jest to, że metody statyczne podobnie jak zwykłe funkcje nie moga być metodami stałymi. Obiekty stałe i metody stałe 18
Przeciażanie metod względem modyfikatora const class PrzykladKlasy //............................................................... public : PrzykladKlasy( ) void Metoda( ) const cout << Metoda ob. stalego << endl; void Metoda( ) cout << Metoda ob. modyfikowalnego << endl; ; //..................................................................................... int main( ) const PrzykladKlasy PrzykladKlasy ObStaly; ObModyf; ObStaly.Metoda( ); ObModyf.Metoda( ); Wynik działania programu: Metoda ob. stalego Metoda ob. modyfikowalnego Modyfikator const wchodzi w skład nazwy metody. Pozwala to przeciażać metody ze względu na ten modyfikator. Przy wywoływaniu metod zawsze wywoływana jest metoda posiadajaca maksymalnie dopuszczalne uprawnienia co do danego typu obiektu. Obiekty stałe i metody stałe 19
Przeciażanie metod względem modyfikatora const class PrzykladKlasy //............................................................... char Tab[ROZMIAR TAB]; public : PrzykladKlasy( ) strcpy( Tab, łódka ); const char Tab( ) const return Tab; char Tab( ) return Tab; ; //..................................................................................... int main( ) const PrzykladKlasy ObStaly; cout << ObStaly.Tab( ) << endl; ObStaly.Tab( ) = b ; cout << ObStaly.Tab( ) << endl; Stosowanie modyfikatora const w tego typu przeciażeniach jest wymuszone przez kompilator. Pozwala to w naturalny sposób wspierać ochronę spójności danych. Obiekty stałe i metody stałe 20
Przeciażanie metod względem modyfikatora const class PrzykladKlasy //............................................................... char Tab; public : PrzykladKlasy( ) Tab = new char [ROZMIA TAB]; strcpy( Tab, łódka ); PrzykladKlasy( ) delete [ ] Tab; char Tab( ) const return Tab; // Czy to jest dobrze? ; //..................................................................................... int main( ) const PrzykladKlasy ObStaly; cout << ObStaly.Tab( ) << endl; ObStaly.Tab( ) = b ; cout << ObStaly.Tab( ) << endl; W przypadku struktur stowarzyszonych w sposób dynamiczny o tym jakie wskaźniki i w jakich momentach moga być udostępniane jako stałe decyduje programista. Kompilator wspiera pojęcie stałości tylko w odniesieniu do obiektu rozumianego jako ciagły obszar pamięci. Obiekty stałe i metody stałe 21
Przeciażanie metod względem modyfikatora const class PrzykladKlasy //............................................................... char Tab; public : PrzykladKlasy( ) Tab = new char [ROZMIA TAB]; strcpy( Tab, łódka ); PrzykladKlasy( ) delete [ ] Tab; const char Tab( ) const return Tab; char Tab( ) return Tab; ; //..................................................................................... int main( ) const PrzykladKlasy ObStaly; cout << ObStaly.Tab( ) << endl; ObStaly.Tab( ) = b ; cout << ObStaly.Tab( ) << endl; Programista może decydować, które ze struktur stowarzyszonych z danym obiektem nie powinny ulegać poprzez zdefiniowanie odpowiednich metod stałych. Obiekty stałe i metody stałe 22
Metody stałe Metody stałe z założenia nie powinny modyfikować stanu obiektu (wartości atrybutów i powiazań z innymi obiektami). W tym sensie pojęcie to ma znacznie szersze niż tylko fizyczna zmiana wartości pól obiektu. Pojęcie stałości dotyczy bardziej logicznej niż fizycznej struktury obiektu. Jest ono jedynie wspierane (na tyle dobrze na ile to tylko jest możliwe) przez język C++. Język C++ nie narzucona żelaznego ograniczenia na operacje realizowane przez metodę stała. Mechanizm rzutowań pozostawia furtkę umożliwiajac a dokonanie wyłomu w zakazie zmian stanu obiektu. Pozwala to na zachowanie elastyczności języka. Należy jednak mieć świadomość, że korzystanie z tej furtki w metodach stałych jest zwykle przejawem złego stylu programowania. Dla logicznej spójności programu metody stałe NIE POWINNY wymuszać zmiany stanu obiektu. Obiekty stałe i metody stałe 23
Obiekty stałe Stan obiektu stałego po zainicjalizowaniu nie powinien być modyfikowany. Pojęcie stałości obiektu jest wspierane przez język C++ w ten sposób, że: zabrania się zmiany wartości pól obiektu (sa one tylko do odczytu), zabrania się wywoływania innych metod niż metody stałe (typu const). Język C++ nie narzuca twardych ograniczeń chroniacych obiekty stałe. Mechanizm rzutowań umożliwia wymuszenie zmiany stanu obiektu. Wymuszanie zmiany stanu obiektu stałego jest zwykle przejawem niedbałego lub wręcz złego stylu programowania. Obiekty stałe i metody stałe 24
Pytania i ćwiczenia Dany jest fragment kodu: class LiczbaZespolona public: float re, im; ; int main() LiczbaZespolona Zm; ( (Liczba const)&zm). re = 5; return 0; 1. Czy dokonane rzutowanie jest dopuszczalne? 2. Czy zwykły obiekt można rzutować na obiekt stały? 3. Czy w tym przypadku nastapi poprawne zapisanie wartości 5 do pola re? 4. Jeżeli nie, to jak należy to zapisać aby operacja była poprawna (chodzi o zapis z rzutowaniem i modyfikatorem const)? 5. Jeżeli tak, to jak należy to zapisać aby uniemożliwić taka operację? Obiekty stałe i metody stałe 25