Obiekty i metody stałe
Transkrypt
Obiekty i metody stałe
Obiekty i metody stałe Bogdan Kreczmer ZPCiR IIAiR PWr pokój 307 budynek C3 [email protected] c 2005–2008 Bogdan Kreczmer⋆ Copyright ⋆ Niniejszy dokument zawiera materiały do wykładu na temat programowania obiektowego. Jest on udostepiony ˛ pod warun- kiem 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]; hh hhh hhhh char Tablica2[ZmienInt]; hhh int main() { hhhh hhhh StalaInt = hZmienInt; hhh // Dopuszczalna w ISO/ANSI C++ ale nie w ANSI C // Niedopuszczalne w ISO/ANSI C++ i ANSI C // 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 ∗) char ∗ const wsk; 1111111 0000000 0000000 1111111 0000000 1111111 0000000 1111111 0000000 1111111 1111111111111 0000000000000 0000000000000 1111111111111 0000000000000 1111111111111 0000000000000 1111111111111 0000000000000 1111111111111 0000000000000 1111111111111 w 1111111111111111111111 0000000000000000000000 0000000000000000000000 1111111111111111111111 0000000000000000000000 1111111111111111111111 0000000000000000000000 1111111111111111111111 0000000000000000000000 1111111111111111111111 l ó d k a 0000000000000000000000 1111111111111111111111 0000000000000000000000 1111111111111111111111 w ó d k a l Obiekty stałe i metody stałe 2 Definiowanie stałych i ich znaczenie const char ∗NapisStaly char ∗ const WskStaly char ∗ WskNapisu = "Jakiś łańcuch"; = “Taka inicjalizacja jest ryzykowna”; = “Równie zła inicjalizacja jak powyżej”; int main() { hhh hhhh hh"Inny WskStaly = hhh łańcuch"; WskStaly[1] = ’!’; hh hhh hhhh NapisStaly[3] = ’$’; hhh NapisStaly = "Inny łańcuch"; NapisStaly = WskStaly; hhhh hhhh WskStaly = hNapisStaly; hhh // To podstawienie jest niedopuszczalne. // Tutaj powinna nastapić ˛ katastrofa. // Ta operacja jest niedopuszczalna // Ta operacja jest niedopuszczalna. } Obiekty stałe i metody stałe 3 Definiowanie parametrów stałych char ∗Kopiuj( char ∗ Cel, const char ∗Zrodlo ) /* { hhhh hhhh Zrodlo[2] /* Tego na pewno być nie może h= hh’x’; h ... Zrodlo = Zrodlo + 1; /* Takie operacje dopuszczamy hhhh hhhh hhh h */ */ */ 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 LiczbaZespolona const float re = StalaZesp. re; float im = StalaZesp. im; hh hhhh hhhre = re; StalaZesp. hhh hhh hhh hhhim = im; StalaZesp. hhh . . . ZmienZesp; StalaZesp = ZmienZesp; // Można zainicjalizować // Możemy odczytać wartości // obiektu stałego // 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; hh } hhhh hhhre 20; // Ta operacja jest niedozwolona StalaZesp. hh= h ((LiczbaZespolona&)StalaZesp). im = 20; // Ta już jest poprawna const cast<LiczbaZespolona&>(StalaZesp). im = 20; // A to bardziej poprawne . . . Zmiane˛ 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 float re = StalaZesp.Re(); hhh hhhh hhh float im = StalaZesp.Im(); hh re = ZmienZesp.Re(); im = ZmienZesp.Im(); . . . ZmienZesp; StalaZesp = ZmienZesp; // Tej metody nie możemy użyć // 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; } hh hhhh void Zmien(float r, float i) const { rehh=hr; im i; } hh= h double Modul() const; }; // // BŁAD ˛ 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ć sie˛ 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 { hhh ((( h(h hhh ((h Im() ∗(Im()); // BŁAD ˛ return sqrt(Re()∗Re() +(( } W metodach stałych nie można odwoływać sie˛ 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? void LiczbaZespolona::Zmien(float r, float i) const { const cast<LiczbaZespolona&>(∗this ). re = r; const cast<LiczbaZespolona&>(∗this ). im = i; } // Dzieki ˛ rzutowaniu otrzy// mujemy obiekt zmienny. Brak zezwolenia na zmiane˛ 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: h hh (( h( h( h( ( h{hhTablica[0] = 4; } const void ZmienWTablicy() ( (( 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 hhh (((( h (hhh IntStala; h (((( h const KlasaPusta ObPustyStaly; h hhh (h (((( (h (h h (h h (hhh (h (h ( ( ( h ( ( h h ( ( 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 KlasaPusta IntStala = IntModyf; ObPustyModyf; const KlasaPusta ObPustyStaly = ObPustyModyf; Inicjalizacje˛ 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; Inicjalizacje˛ zmiennej zadeklarowanej jako stała można dokonać poprzez przypisanie wartości stałej w momencie jej deklaracji. W przypadku obiektu za inicjalizacje˛ 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˛eścia˛ obiektu. Stanowia˛ one raczej cz˛eść 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; hhh ((( (((hhh const { hreturn PoleStatyczne; } static int Wez( )(( static void Zmien(int Wartosc ) { PoleStatyczne = Wartosc; } hh ( }; 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 wiec ˛ posługujac ˛ sie˛ obiektem stałym wywoływać metode˛ 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 wzgledem ˛ 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. Metoda ob. stalego modyfikowalnego Modyfikator const “wchodzi” w skład nazwy metody. Pozwala to przeciażać ˛ metody ze wzgledu ˛ 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 wzgledem ˛ 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; ( hh( ( h h h ’b’; ∗ObStaly.Tab(( )(=h( cout << ObStaly.Tab( ) << endl; } Stosowanie modyfikatora const w tego typu przeciażeniach ˛ jest wymuszone przez kompilator. Pozwala to w naturalny sposób wspierać ochrone˛ spójności danych. Obiekty stałe i metody stałe 20 Przeciażanie ˛ metod wzgledem ˛ 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ć udostepniane ˛ jako stałe decyduje programista. Kompilator wspiera pojecie ˛ stałości tylko w odniesieniu do obiektu rozumianego jako ciagły ˛ obszar pamieci. ˛ Obiekty stałe i metody stałe 21 Przeciażanie ˛ metod wzgledem ˛ 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; ( hh( ( h h h ’b’; ∗ObStaly.Tab( ( ) (=h( 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 poje˛ cie to ma znacznie szersze niż tylko fizyczna zmiana wartości pól obiektu. Pojecie ˛ 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 jezyk ˛ C++. C++ nie narzucona żelaznego ograniczenia na operacje realiJezyk ˛ zowane przez metode˛ stała. ˛ Mechanizm rzutowań pozostawia furtk˛e umożliwiajac ˛ a˛ dokonanie wyłomu w zakazie zmian stanu obiektu. Pozwala to na zachowanie elastyczności jezyka. ˛ 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. Poje˛ cie stałości obiektu jest wspierane przez jezyk ˛ C++ w ten sposób, że: • zabrania sie˛ zmiany wartości pól obiektu (sa˛ one tylko do odczytu), • zabrania sie˛ wywoływania innych metod niż metody stałe (typu const). Jezyk ˛ 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 wrecz ˛ 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˛ operacje? ˛ Obiekty stałe i metody stałe 25