Klasa, metody, rozwijanie w linii 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.
Jeszcze funkcja Metody klasy Już metoda struct LiczbaZespolona ; void sprzezenie( LiczbaZespolona wlzesp ) wlzesp >im = wlzesp >im; struct LiczbaZespolona ; void Sprzezenie( ); void LiczbaZespolona::Sprzezenie( ) im = im; LiczbaZespolona LZesp; LiczbaZespolona LZesp; LZesp.re = 5; LZesp.im = -6; sprzezenie(&lzesp); LZesp.re = 5; LZesp.im = -6; LZesp.Sprzezenie( ); Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 1
Metody klasy Struktura i funkcja Struktura i metoda struct LiczbaZespolona re: im: struct LiczbaZespolona re: im: void Sprzezenie( wlzesp ) void Sprzezenie( ) this int main() LZesp re: im: wlzesp >im = wlzesp >im int main() LZesp re: im: this >im = this >im Sprzezenie( &LZesp ) (&LZesp) >im = (&LZesp) >im LZesp.Sprzezenie() this this >im = this >im Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 2
Jeszcze funkcja struct LiczbaZespolona ; Metody klasy Już metoda struct LiczbaZespolona ; void Zmien( float r, float i ); void zmien(liczbazespolona warg, float r, float i ) warg->re = r; warg->im = i; void LiczbaZespolona::Zmien(float r, float i) re = r; im = i; LiczbaZespolona LZesp; LiczbaZespolona LZesp; zmien(&lzesp, 5, -6); LZesp.Zmien(5, -6); Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 3
Metody klasy - wartości domyślne struct LiczbaZespolona ; void Zwieksz( float r = 1, float i = 1 ); void LiczbaZespolona::Zwieksz( float r, float i ) re += r; im += i; LiczbaZespolona LZesp.Zwieksz( ); LZesp.Zwieksz( 2 ); LZesp.Zwieksz( 1, -1 ); LZesp; Wartości domyślne metod sa tak samo traktowane jak wartości domyślne funkcji. Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 4
Metody klasy - wartości domyślne struct LiczbaZespolona ; void Zwieksz( float r = 1, float i = 1 ); void LiczbaZespolona::Zwieksz( float r = 1, float i = 1 ) re += r; im += i; LiczbaZespolona LZesp.Zwieksz( ); LZesp.Zwieksz( 2 ); LZesp.Zwieksz( 1, -1 ); LZesp; Wartości domyślne moga wystapić tylko raz w deklaracji metody w ciele definicji struktury/klasy. Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 5
Metody klasy - wartości domyślne struct LiczbaZespolona ; void Zmien( float r, float i ); void Zmien( float ri ); void LiczbaZespolona::Zmien( float r, float i ) re = r im = i; void LiczbaZespolona::Zmien( float ri ) re = im = ri; LiczbaZespolona LZesp.Zmien( 2 ); LZesp.Zmien( 1, -1 ); LZesp; Podobnie jak zwykłe funkcje można również przeciażać metody. Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 6
Metody klasy - wartości domyślne struct LiczbaZespolona ; void Zmien( float r, float i = 0 ); void Zmien( float ri ); void LiczbaZespolona::Zmien( float r, float i ) re = r im = i; void LiczbaZespolona::Zmien( float ri ) re = im = ri; LiczbaZespolona LZesp.Zmien( 2 ); LZesp.Zmien( 1, -1 ); LZesp; Również i w tym przypadku konieczne jest zadbanie o jednoznaczność przeciażeń. Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 7
Jeszcze funkcja struct LZespolona ; LZespolona dodaj( LZespolona Z1, LZespolona Z2 ) Z2.re += Z1.re; Z2.im += Z1.im; return Z2; Metody klasy struct LZespolona Już metoda LZespolona Dodaj( LZespolona Z2 ); ; LZespolona LZespolona::Dodaj( LZespolona Z2 ) Z2.re += re; Z2.im += im; return Z2; LZespolona lz1, lz2; LZespolona lz1, lz2; lz2 = dodaj( lz1, lz2 ); lz2 = lz1.dodaj( lz2 ); Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 8
Metody klasy Jeszcze funkcja struct LZespolona ; struct LZespolona Już metoda LZespolona operator + ( LZespolona Z ); ; LZespolona operator + ( LZespolona Z1, LZespolona Z2 ) Z2.re += Z1.re; Z2.im += Z1.im; return Z2; LZespolona LZespolona::operator + ( LZespolona Z2 ) Z2.re += re; Z2.im += im; return Z2; LZespolona lz1, lz2; LZespolona lz1, lz2; lz2 = lz1 + lz2; lz2 = lz1 + lz2; Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 9
struct LZespolona ; struct void Sprzezenie( ); void Zmien( float r, float i ); LZespolona operator + ( LZespolona Z ); Równoważność definicji = class LZespolona ; class void Sprzezenie( ); void Zmien( float r, float i ); LZespolona operator + ( LZespolona Z ); struct NazwaKlasy... = class NazwaKlasy... ; ; Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 10
C++ Reprezentacja w UML Klasa UML class LZespolona ; void Sprzezenie( ); void Zmien( float r, float i ); LZespolona operator + ( LZespolona Z ); LZespolona +re: float +im: float +Sprzezenie() +Zmien(r:float,i:float) +operator + (Z2:LZespolona): LZespolona Obiekt Arg2 :LZespolona LZespolona Arg2; Arg2 :LZespolona [nie zainicjalizowany] re :float im :float Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 11
Konstruktory i destruktory class LZespolona //.................................................... float re, im; LZespolona( ) re = im = 0; // Konstruktor LZespolona( float r, float i ) re = r; im = i; // Konstruktor LZespolona( ) // Destruktor ; //....................................................................... LZespolona Z1; LZespolona Z2(2,0);... Oprócz zwykłych metod można zdefiniować w każdej klasie specjalne metody nazywane konstruktorami i destruktorami. Sa one wywoływane niejawnie odpowiednio w momencie tworzenia i destrukcji obiektu. Konstruktory można przeciażać, jak też stosować argumenty domyślne. Destruktor jest zawsze tylko jeden (bezparametryczny). Wywołanie odpowiedniego konstruktora można jawnie wymusić w deklaracji zmiennej. Destruktory maja istotne znaczenie w przypadku występowania pól wskaźnikowych. Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 12
Obiekty jako pola class Wektor //.............................................. float x, y; ; //............................................................. class Prostokat //........................................... Wektor Rog GornyLewy, Rog DolnyPrawy; ; //............................................................. Prostokat Pr; Pr.Rog GornyLewy.x = 0; Pr.Rog GornyLewy.y = 100; Pr.Rog DolnyPrawy.x = 100; Pr.Rog DolnyPrawy.y = 1; Obiekty danej klasy moga być polami obiektów innej klasy. Ich konstrukcja jest analogiczna jak konstrukcja struktur w języku C z wykorzystaniem pól będacych również strukturami. Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 13
Reprezentacja zależności class Wektor //.............................. float x, y; ; //............................................. Wektor +x: float +y: float class Prostokat //............................ Wektor Rog GornyLewy; Wektor Rog DolnyPrawy; ; //............................................. Prostokat +Rog_GornyLewy: Wektor +Rog_DolnyPrawy: Wektor Wykorzystujac diagram klas rysowany w języku UML można graficznie przedstawić zależności między klasami. Pozwala to określić, czy zmiany w danej klasie moga skutkować koniecznościa zmian również w innych klasach. Takie informacje bezpośrednio z kodu programu sa trudne do odczytania. Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 14
Metody rozwijane w linii class LZespolona class LZespolona ; void Sprzezenie( ) im = im; ; inline void Sprzezenie( ); inline void LZespolona::Sprzezenie( ) im = im; LiczbaZespolona LZesp; LiczbaZespolona LZesp; LZesp.Sprzezenie( ); LZesp.Sprzezenie( ); Specyfikator inline umożliwia rozwinięcie kodu metod i przeciażeń operatorów w miejscu ich wywołania. Metody i przeciażenia operatorów definiowane w ciele klasy sa domyślnie rozwijane w linii, o ile nie zostanie to zmienione za pomoca opcji kompilatora, np -fno-default-inline dla g++. Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 15
Metody rozwijane w linii class LZespolona ; void Sprzezenie( ) im = im; class LZespolona void Sprzezenie( ) im = im; ; Definicja metody lub przeciażenie operatora rozwijane w linii w ciele definicji klasy może znajdować się zarówno przed, jak też po deklaracji pól danej klasy. Nie jest wówczas błędem odwoływanie się do pól przed ich formalnym zadeklarowaniem. Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 16
Funkcje rozwijane w linii inline double poteguj( double Wykladnik, unsigned int Potega )... Tak jak metody, funkcje oraz przeciażenia operatorów również moga być rozwijane w miejscu ich wywołania. inline double poteguj( double Wykladnik, unsigned int Potega ) return (Potega == 0)? 1 : Wykladnik poteguj(potega-1);... Wynik = poteguj(4,3); Sposób rozwinięcia wywołania funkcji zależy od inteligencji kompilatora. Dla wywołania przedstawionego powyżej rozwinięcie kodu może sprowadzić się do wstawienia wartości 64. W innym przypadku rozwinięcie może mieć postać: 4 potega(2); Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 17
Metody rozwijane w linii plik1.cpp inline double poteguj( double, unsigned int); plik2.cpp inline double poteguj( double w, unsigned int p ) return (p == 0)? 1 : w poteguj(p - 1); double Wynik = poteguj(12, 3); Funkcja rozwijana w linii wywołania powinna występować w tej samej jednostce translacyjnej, w której występuje jej wywołanie. Kompilator może dysponować dodatkowymi udogodnieniami pozwalajacymi na umieszczenie funkcji w innej jednostce. Jednak nie jest to reguła. Aby zagwarantować przenośność należy nie uwzględniać tych dodatkowych udogodnień. Nie jest konieczne natomiast, aby definicja występowała przed jej wywołaniem. Jednak jest to zalecane (nie każdy kompilator może sobie z tym poradzić). Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 18
Metody rozwijane w linii plik1.cpp plik2.cpp // Ta definicja funkcji zawiera bład. inline double poteguj( double w, unsigned int p ); double rezultat = 0; for ( ; p ; p) rezultat = rezultat w; return w; inline double poteguj( double w, unsigned int p ) return (p == 0)? 1 : w poteguj(p - 1); Jeżeli funkcja rozwijana w linii występuje pod ta sama nazwa w kilku jednostkach translacyjnych, to jej definicja powinna być taka sama. Przykład powyżej prezentuje błędne realizację implementacji tej funkcji. W jednym z plików funkcja ta zawiera bład. Tego rodzaju pomyłki sa bardzo trudne do debugowia (wydaje się, że funkcja raz działa poprawnie, a raz nie). Z tego powodu zalecane jest umieszczanie definicji tego rodzaju funkcji w plikach nagłówkowych. Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 19
Metody rozwijane - ważniejsze uwagi Specyfikator inline nie jest dla kompilatora poleceniem rozwinięcia funkcji lub metody w linii wywołania. Pełni on jedynie rolę zalecenia, które powinno być w miarę możliwości uwzględnione. Kompilator przy podejmowaniu decyzji o rozwinięciu kodu funkcji kierować się może heurystycznymi ocenami uzwględniajacymi, np. rozmiar kodu rozwijanej funkcji i/lub ilość dokonanych rozwinięć w aktualnie kompilowanej funkcji. Rozwinięcie funkcji nie zawsze musi oznaczać zwiększenie rozmiaru kodu (aczkolwiek zazwyczaj tak jest). Stosowanie częstych rozwinięć funkcji nie zawsze musi oznaczać przyśpieszenie wykonywania kodu (aczkolwiek zazwyczaj tak jest). Specyfikator inline nie wpływa na znaczenie funkcji; funkcja rozwijana w miejscu wywołania nadal ma unikatowy adres. Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 20
Pytania i ćwiczenia 1. Czy funkcja main może być rozwijana w linii? 2. Jeżeli w funkcji występuja zmienne statyczne, to czy można do takiej funkcji zastosować specyfikator inline? 3. Czy w danej funkcji, do której zastosowano specyfikator inline można wywołać inna funkcje, do której również zastosowano ten specyfikator? Jakie to będzie miało konsekwencje dla postaci kodu? Copyright c 2005 2008 Bogdan Kreczmer Klasa, metody, rozwijanie w linii 21