float
Transkrypt
float
Klasa, metody, rozwijanie w linii 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 ˛ w całości, razem z warunkiem wykorzystania wyłacznie ˛ do własnych prywatnych potrzeb i może on być kopiowany wyłacznie niniejsza˛ strona˛ tytułowa. ˛ 1 Metody klasy Jeszcze funkcja struct LiczbaZespolona { float re; float im; }; Już metoda struct LiczbaZespolona { float re; float im; void Sprzezenie( ); }; void sprzezenie( LiczbaZespolona ∗wLZesp ) { wLZesp–>im = – wLZesp–>im; } void LiczbaZespolona::Sprzezenie( ) { im = – im; } int main( ) { LiczbaZespolona int main( ) { LiczbaZespolona LZesp; LZesp.re = 5; LZesp.im = -6; sprzezenie(&LZesp); } LZesp; LZesp.re = 5; LZesp.im = -6; LZesp.Sprzezenie( ); } Klasa, metody, rozwijanie w linii 2 Metody klasy Struktura i funkcja Struktura i metoda Klasa, metody, rozwijanie w linii 3 Metody klasy Jeszcze funkcja struct LiczbaZespolona { float re; float im; }; Już metoda struct LiczbaZespolona { float re; float im; 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; } int main( ) { LiczbaZespolona int main( ) { LiczbaZespolona LZesp; LZesp.Zmien(5, -6); zmien(&LZesp, 5, -6); } LZesp; } Klasa, metody, rozwijanie w linii 4 Metody klasy - wartości domyślne struct LiczbaZespolona { float re; float im; void Zwieksz( float r = 1, float i = 1 ); }; void LiczbaZespolona::Zwieksz( float r, float i ) { re += r; im += i; } int main( ) { LiczbaZespolona } LZesp; LZesp.Zwieksz( ); LZesp.Zwieksz( 2 ); LZesp.Zwieksz( 1, -1 ); Wartości domyślne metod sa˛ tak samo traktowane jak wartości domyślne funkcji. Klasa, metody, rozwijanie w linii 5 Metody klasy - wartości domyślne struct LiczbaZespolona { float re; float im; void Zwieksz( float r = 1, float i = 1 ); }; ( ( hh( hh( ( h( h( ( h hh h float i (( void LiczbaZespolona::Zwieksz( float r ( =h1, =h1( ) { re += r; im += i; } int main( ) { LiczbaZespolona } LZesp; LZesp.Zwieksz( ); LZesp.Zwieksz( 2 ); LZesp.Zwieksz( 1, -1 ); Wartości domyślne moga˛ wystapić ˛ tylko raz w deklaracji metody w ciele definicji struktury/klasy. Klasa, metody, rozwijanie w linii 6 Metody klasy - wartości domyślne struct LiczbaZespolona { float re; float im; 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; } int main( ) { LiczbaZespolona } LZesp; LZesp.Zmien( 2 ); LZesp.Zmien( 1, -1 ); Podobnie jak zwykłe funkcje można również przeciażać ˛ metody. Klasa, metody, rozwijanie w linii 7 Metody klasy - wartości domyślne struct LiczbaZespolona { float re; float im; h( h ( h h void Zmien( float r, float i (h =( 0( ); void Zmien( float ri ); h ( }; void LiczbaZespolona::Zmien( float r, float i ) { re = r im = i; } void LiczbaZespolona::Zmien( float ri ) { re = im = ri; } int main( ) { LiczbaZespolona } LZesp; LZesp.Zmien( 2 ); LZesp.Zmien( 1, -1 ); Również i w tym przypadku konieczne jest zadbanie o jednoznaczność przeciaże ˛ ń. Klasa, metody, rozwijanie w linii 8 Metody klasy Jeszcze funkcja struct LZespolona { float re; float im; }; Już metoda struct LZespolona { float re; float im; LZespolona Dodaj( LZespolona Z2 ); }; LZespolona dodaj( LZespolona Z1, LZespolona Z2 ) { Z2.re += Z1.re; Z2.im += Z1.im; return Z2; } LZespolona LZespolona::Dodaj( LZespolona Z2 ) { Z2.re += re; Z2.im += im; return Z2; } int main( ) { LZespolona int main( ) { LZespolona lz1, lz2; lz2 = dodaj( lz1, lz2 ); } lz1, lz2; lz2 = lz1.Dodaj( lz2 ); } Klasa, metody, rozwijanie w linii 9 Metody klasy Jeszcze funkcja struct LZespolona { float re; float im; }; Już metoda struct LZespolona { float re; float im; LZespolona operator + ( LZespolona Z ); }; LZespolona operator + ( LZespolona Z1, LZespolona Z2 ) { Z2.re += Z1.re; Z2.im += Z1.im; return Z2; } int main( ) { LZespolona lz2 = lz1 + lz2; } lz1, lz2; LZespolona LZespolona::operator + ( LZespolona Z2 ) { Z2.re += re; Z2.im += im; return Z2; } int main( ) { LZespolona lz1, lz2; lz2 = lz1 + lz2; } Klasa, metody, rozwijanie w linii 10 Równoważność definicji struct class struct LZespolona { float re; float im; void Sprzezenie( ); void Zmien( float r, float i ); LZespolona operator + ( LZespolona Z ); class LZespolona { public: float re; float im; = void Sprzezenie( ); void Zmien( float r, float i ); LZespolona operator + ( LZespolona Z ); }; }; struct NazwaKlasy { ... }; class NazwaKlasy { public: = ... }; Klasa, metody, rozwijanie w linii 11 Reprezentacja w UML C++ UML Klasa class LZespolona { public: float re; float im; void Sprzezenie( ); void Zmien( float r, float i ); LZespolona operator + ( LZespolona Z ); }; Klasa, metody, rozwijanie w linii 11 Reprezentacja w UML C++ UML Klasa class LZespolona { public: float re; float im; void Sprzezenie( ); void Zmien( float r, float i ); LZespolona operator + ( LZespolona Z ); }; Obiekt LZespolona Arg2; Klasa, metody, rozwijanie w linii 12 Konstruktory i destruktory class LZespolona { //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public: float re, im; LZespolona( ) { re = im = 0; } LZespolona( float r, float i ) { re = r; im = i; } // Konstruktor // Konstruktor ˜LZespolona( ) { } // Destruktor }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { LZespolona LZespolona ... } Z1; 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 wystepowania ˛ pól wskaźnikowych. Klasa, metody, rozwijanie w linii 13 Obiekty jako pola class Wektor { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public: float x, y; }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class Prostokat { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public: Wektor Rog GornyLewy, Rog DolnyPrawy; }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . int main( ) { 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 jezyku ˛ C z wykorzystaniem pól bed ˛ acych ˛ również strukturami. Klasa, metody, rozwijanie w linii 14 Reprezentacja zależności class Wektor { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public: float x, y; }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class Prostokat { //. . . . . . . . . . . . . . . . . . . . . . . . . . . . public: Wektor Rog GornyLewy; Wektor Rog DolnyPrawy; }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wykorzystujac ˛ diagram klas rysowany w jezyku ˛ UML można graficznie przedstawić zależności mie˛ 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. Klasa, metody, rozwijanie w linii 15 Metody rozwijane w linii class LZespolona { public: float re; float im; class LZespolona { public: float re; float im; inline void Sprzezenie( ); void Sprzezenie( ) { im = – im; } }; inline void LZespolona::Sprzezenie( ) { im = – im; } }; int main( ) { LiczbaZespolona LZesp; int main( ) { LiczbaZespolona LZesp.Sprzezenie( ); } LZesp; LZesp.Sprzezenie( ); } Specyfikator inline umożliwia rozwiniecie ˛ 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++. Klasa, metody, rozwijanie w linii 16 Metody rozwijane w linii class LZespolona { public: float re; float im; void Sprzezenie( ) { im = – im; } }; class LZespolona { public: void Sprzezenie( ) { im = – im; } float re; float im; }; Definicja metody lub przeciażenie ˛ operatora rozwijane w linii w ciele definicji klasy może znajdować sie˛ zarówno przed, jak też po deklaracji pól danej klasy. Nie jest wówczas błedem ˛ odwoływanie sie˛ do pól przed ich formalnym zadeklarowaniem. Klasa, metody, rozwijanie w linii 17 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 rozwiniecia ˛ wywołania funkcji zależy od inteligencji kompilatora. Dla wywołania przedstawionego powyżej rozwiniecie ˛ kodu może sprowadzić sie˛ do wstawienia wartości 64. W innym przypadku rozwiniecie ˛ może mieć postać: 4∗potega(2); Klasa, metody, rozwijanie w linii 18 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); } int main( ) { double Wynik = poteguj(12, 3); } Funkcja rozwijana w linii wywołania powinna wystepować ˛ w tej samej jednostce translacyjnej, w której wystepuje ˛ 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 uwzgledniać ˛ tych dodatkowych udogodnień. Nie jest konieczne natomiast, aby definicja wystepowała ˛ przed jej wywołaniem. Jednak jest to zalecane (nie każdy kompilator może sobie z tym poradzić). Klasa, metody, rozwijanie w linii 19 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 wystepuje ˛ pod ta˛ sama˛ nazwa˛ w kilku jednostkach translacyjnych, to jej definicja powinna być taka sama. Przykład powyżej prezentuje błedne ˛ realizacje˛ implementacji tej funkcji. W jednym z plików funkcja ta zawiera bład. ˛ Tego rodzaju pomyłki sa˛ bardzo trudne do debugowia (wydaje sie, ˛ że funkcja raz działa poprawnie, a raz nie). Z tego powodu zalecane jest umieszczanie definicji tego rodzaju funkcji w plikach nagłówkowych. Klasa, metody, rozwijanie w linii 20 Metody rozwijane - ważniejsze uwagi • Specyfikator inline nie jest dla kompilatora poleceniem rozwiniecia ˛ funkcji lub metody w linii wywołania. Pełni on jedynie role˛ zalecenia, które powinno być w miare˛ możliwości uwzglednione. ˛ Klasa, metody, rozwijanie w linii 20 Metody rozwijane - ważniejsze uwagi • Specyfikator inline nie jest dla kompilatora poleceniem rozwiniecia ˛ funkcji lub metody w linii wywołania. Pełni on jedynie role˛ zalecenia, które powinno być w miare˛ możliwości uwzglednione. ˛ • Kompilator przy podejmowaniu decyzji o rozwinieciu ˛ kodu funkcji kierować sie˛ może heurystycznymi ocenami uzwgledniaj ˛ acymi, ˛ np. rozmiar kodu rozwijanej funkcji i/lub ilość dokonanych rozwinieć ˛ w aktualnie kompilowanej funkcji. Klasa, metody, rozwijanie w linii 20 Metody rozwijane - ważniejsze uwagi • Specyfikator inline nie jest dla kompilatora poleceniem rozwiniecia ˛ funkcji lub metody w linii wywołania. Pełni on jedynie role˛ zalecenia, które powinno być w miare˛ możliwości uwzglednione. ˛ • Kompilator przy podejmowaniu decyzji o rozwinieciu ˛ kodu funkcji kierować sie˛ może heurystycznymi ocenami uzwgledniaj ˛ acymi, ˛ np. rozmiar kodu rozwijanej funkcji i/lub ilość dokonanych rozwinieć ˛ w aktualnie kompilowanej funkcji. • Rozwiniecie ˛ funkcji nie zawsze musi oznaczać zwiekszenie ˛ rozmiaru kodu (aczkolwiek zazwyczaj tak jest). Klasa, metody, rozwijanie w linii 20 Metody rozwijane - ważniejsze uwagi • Specyfikator inline nie jest dla kompilatora poleceniem rozwiniecia ˛ funkcji lub metody w linii wywołania. Pełni on jedynie role˛ zalecenia, które powinno być w miare˛ możliwości uwzglednione. ˛ • Kompilator przy podejmowaniu decyzji o rozwinieciu ˛ kodu funkcji kierować sie˛ może heurystycznymi ocenami uzwgledniaj ˛ acymi, ˛ np. rozmiar kodu rozwijanej funkcji i/lub ilość dokonanych rozwinieć ˛ w aktualnie kompilowanej funkcji. • Rozwiniecie ˛ funkcji nie zawsze musi oznaczać zwiekszenie ˛ rozmiaru kodu (aczkolwiek zazwyczaj tak jest). • Stosowanie cz˛estych rozwinieć ˛ funkcji nie zawsze musi oznaczać przyśpieszenie wykonywania kodu (aczkolwiek zazwyczaj tak jest). Klasa, metody, rozwijanie w linii 20 Metody rozwijane - ważniejsze uwagi • Specyfikator inline nie jest dla kompilatora poleceniem rozwiniecia ˛ funkcji lub metody w linii wywołania. Pełni on jedynie role˛ zalecenia, które powinno być w miare˛ możliwości uwzglednione. ˛ • Kompilator przy podejmowaniu decyzji o rozwinieciu ˛ kodu funkcji kierować sie˛ może heurystycznymi ocenami uzwgledniaj ˛ acymi, ˛ np. rozmiar kodu rozwijanej funkcji i/lub ilość dokonanych rozwinieć ˛ w aktualnie kompilowanej funkcji. • Rozwiniecie ˛ funkcji nie zawsze musi oznaczać zwiekszenie ˛ rozmiaru kodu (aczkolwiek zazwyczaj tak jest). • Stosowanie cz˛estych rozwinieć ˛ 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. Klasa, metody, rozwijanie w linii 21 Pytania i ćwiczenia 1. Czy funkcja main może być rozwijana w linii? 2. Jeżeli w funkcji wystepuj ˛ a˛ 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 bedzie ˛ miało konsekwencje dla postaci kodu? Klasa, metody, rozwijanie w linii