1 / 81

Objektově orientované programování

RND r. Jan Lánský, Ph.D. Katedra softwarového inženýrství MFF UK Katedra informatiky V Š FS (Autor původní verze slajd ů : Mgr. Zbyněk Winkler ) (Autor prapůvodní verze slajd ů : RND r. Filip Zavoral, Ph.D. ) ( Část slajdů převzata od : RND r. David Bednárek ) zizelevak@gmail.com

errol
Télécharger la présentation

Objektově orientované programování

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. RNDr. Jan Lánský, Ph.D. Katedra softwarového inženýrství MFF UK Katedra informatiky VŠFS (Autor původní verze slajdů:Mgr. Zbyněk Winkler) (Autor prapůvodní verze slajdů:RNDr. Filip Zavoral, Ph.D.) (Část slajdů převzata od:RNDr. David Bednárek) zizelevak@gmail.com http://kocour.ms.mff.cuni.cz/~lansky/ Objektově orientované programování

  2. Studijní povinnosti Zápočet Aktivní účast na cvičeních (80 %) Chybějící účast lze nahradit: Vypracováním a předvedením příkladů probraných na cvičení [v semestru] Zápočtový test (praktický příklad, odladěný, 3 hodiny) [kdykoliv] Zápočtový program Téma (do 2. přednášky) Specifikace (do 3. přednášky) Betaverze (do konce semestru) Finální verze (do 30.6.) Zkouška Ústní, s přípravou na PC (2 hodiny) Na zkoušku lze jít i bez zápočtu

  3. Literatura Miroslav Virius: Programování v C++ Miroslav Virius: Pasti a propasti jazyka C++ Miroslav Virius: Od C k C++ Scott Meyers: Effective C++, More Effective C++, Effective STL Herb Sutter: Exceptional C++, More Exceptional C++ Que: ANSI/ISO C++ Professional Programmer's Handbook Bruce Eckel: Myslíme v jazyce C++ James O. Coplien: Advanced C++ Programming Styles and Idioms Bjarne Stroustrup: The C++ Programming Language ISO/IEC 14882, ANSI: Programming languages - C++ (1998, 2003)

  4. ZS (PJC) LS (OOP) Obsah předmětu C++ C++ C C Paradigmata programování, OOP Objekty, zapouzdření, dědičnost, konstruktory a destruktory Přetěžování funkcí, předefinování operátorů Pozdní vazba, virtuální funkce Abstraktní datové typy Šablony, výjimky, prostory jmen. Objektové knihovny: streams, STL RTTI, OO styly a idiomy...

  5. Paradigmata programování side effects, údržba nelze rozumně rozšiřovat dále – generické programování šablony, STL Procedurální programování • jakou akci mám provést • vstup – výpočet (algoritmus) – výstup • black box: procedura / funkce Modulární programování • rozdělení problému na komponenty • procedury pracují nad daty - rozhraní • black box: modul Datová abstrakce • vytvoření vlastního datového typu (abstract/user defined datové typy) • kompletní množina operací nad tímto typem • black box: datový typ Objektové programování • dědičnost – obecné / konkrétní vlastnosti • Polymorfismus – odlišné chování potomků • možnost pozdějších rozšíření • zapouzdření

  6. Koncepční pohled objekt: entita reagující na vnější podněty třída: množina stejně reagujících entit Technický pohled objekt: struktura obsahující data a funkce, instance třídy (proměnná) třída: typ objektu – jednotná struktura dat, stejné operace nad daty Zobecnění pojmu struktura (struct) Rozdíl mezi class a struct v C++ je nepatrný, užívání class je pouze konvence deklarace třídy obsahuje Deklarace datových položek (stejně jako v C) Funkce (metody), virtuální funkce a statické funkce Definice výčtových konstant a typů (včetně vnořených tříd) Rozhraní – veřejné informace a služby pro uživatele Implementace – (neveřejná) interní data a metody (funkce) Třídy a objekty

  7. Třída zvíře v C++ - rozhraní definice třídy class Zvire { private: int zaludek; public: Zvire() { zaludek = 1; }; int zije() { return zaludek>0; }; int jez( int jidlo); int vymesuj( int objem); }; zvire.h rozhraní (veřejné) vnitřní stav (privátní) Datová položka konstruktor (inicializace) inline tělo funkce metody Deklarace metody

  8. Třída zvíře - implementace class Zvire { private: int zaludek; public: Zvire() { ... }; int zije() { ... }; int jez( int jidlo); int vymesuj( int objem); }; #include ”zvire.h” int Zvire::jez( int jidlo) { if( ! zije()) return 0; return zaludek += jidlo; } int Zvire::vymesuj( int objem) { if( (zaludek -= objem) <= 0) zaludek = 0; return zaludek; } zvire.h zvire.cpp :: operátor kvalifikace Třída metody Implementace (tělo) metody Přístup k datům metody Středník !!!

  9. Třída zvíře - použití class Zvire { private: int zaludek; public: Zvire() { ... }; int zije() { ... }; int jez( int jidlo); int vymesuj( int objem); }; #include ”zvire.h” ..... { ..... Zvire pytlik; pytlik.jez(5); pytlik.vymesuj(3); if( ! pytlik.zije()) return -1; pytlik.vymesuj(4); if( ! pytlik.jez(1)) return -2; ..... } -1  0  mujprogram.cpp zvire.h Import rozhraní Automatický konstruktor zaludek = 1 Instance třídy = objekt zaludek = 6 zaludek = 3

  10. Objekt - instance třídy int Zvire::jez( int jidlo) { if( ! zije()) return 0; return zaludek += jidlo; } ..... Zvire pytlik, beruska; pytlik.jez( 5); beruska.jez( 1); ..... zaludek 6 zaludek 2 dvě instance třídy Metoda třídy - ke kterému objektu má přistupovat? ? pytlik: beruska:

  11. this int jez( Zvire* this, int jidlo) { if( ! zije( this)) return 0; return this->zaludek += jidlo; } ..... Zvire pytlik, beruska; jez( &pytlik, 5); jez( &beruska, 1); ..... int Zvire::jez( int jidlo) { if( ! zije()) return 0; return zaludek += jidlo; } ..... Zvire pytlik, beruska; pytlik.jez( 5); beruska.jez( 1); ..... zaludek 6 zaludek 2 Každá metoda dostane 'tajný'parametr this – ukazatel na objekt zvire:: znamena zvire * this C C++ this->zije() this->zaludek this pytlik: beruska:

  12. Reference reference pouze inicializacenelze měnit int x = 1, y = 2; int *px; px = &x; *px = 3; int &ry = y; ry = 4; return *px + ry; 3 4 reference i ukazatele jsou reprezentovány adresou swap( int& a, int& b) { int c = a; a = b; b = c; } int x = 1, y = 2; swap( x, y); skutečné parametry odkazy na proměnné 1 2 zpřehlednění kódu přetěžování funkcí x: :px y: :ry x: :a y: :b

  13. Přetěžování funkcí Funkce je definována svým identifikátorem a počtem a typem parametrů int pocitej( int x) { return x+1; } int pocitej( int a, int b) { return 2*a + b; } int pocitej( int a, const char* s) { return a + strlen( s); } pocitej( 1); // int pocitej( int) pocitej( 1, 2); // int pocitej( int, int) pocitej( 1, "ahoj"); // int pocitej( int, char*) Funkce se stejným identifikátorem ale různým počtem parametrů Funkce se stejným počtem ale různým typem parametrů Správná funkce podle počtu a typů skutečných parametrů

  14. Implicitní parametry Některé parametry funkce mohou mít implicitní hodnoty • pokud nejsou všechny parametry implicitní – implicitní parametry odzadu Při volání funkce lze implicitní parametry vynechat • použije se implicitní hodnota Kdy použít přetěžování a kdy implicitní parametry? • Stejný kód pro různý počet parametrů implicitní parametry • Pro různé počty nebo typy parametrů různý kód přetěžování int fce( int a, int b = 2, int c = 4) { return 2*a + b - c; } fce( 1); // int fce( 1, 2, 4) fce( 1, 5); // int fce( 1, 5, 4) fce( 1, 5, 6); // int fce( 1, 5, 6) Volá se stále stejná funkce int fce( int, int, int)

  15. Konstruktory class Zvire { private: int zaludek; public: Zvire() { zaludek = 1; }; Zvire( int zal) { zaludek = zal; }; Zvire( const Zvire& vzor) { zaludek = vzor.zaludek; }; }; Zvire beruska; Zvire pytlik( 20); Zvire beberuska( beruska); Zvire tlustoch = pytlik; různé zápisy copy konstruktoru Pro U≠T nejsou zcela ekvivalentní: U u; T t(u); // T::T( U&) T t = u; // T::T( T(u)) nebo // T::T( u.operator T()) zatím lze ignorovat Implicitní konstruktor bez parametrů Konstruktor s parametry Copy konstruktor X (const X&) vytvoří objekt jako kopii jiného

  16. Konstruktor s parametry class Clovek { private: char jmeno[50]; Clovek(); public: //Clovek() { jmeno[0] = 0; }; Clovek( char *jmeno) { strcpy(this->jmeno, jmeno); }; }; Clovekhonza("Honza"); //Clovek petr; Tímto zakážeme deklarovat objekt bez použití NEimplicitního konstruktoru • Využití • Předání nastavení objektu • Šetří řádky kódu • Zakázat implicitiní konstruktor • Dobře rozvážit zda zakázat • Bezpečnost proti nezadání klíčové hodnoty • identifikátor Nejde, zakázali jsme

  17. Přetěžování operátorů - deklarace implicitní parametry  implicitní konstruktor class Bod { private: int x, y; public: Bod( int xx=0, int yy=0) { x=xx; y=yy; }; Bod operator+( const Bod&); Bod operator=( const Bod&); }; Bod a(1,2), b, c; c = a + b; přetížení operátoru + a + b  a.operator+(b) a = b  a.operator=(b) Bod::Bod(0,0); c.operator=(a.operator+(b)); c.assign( a.add( b));

  18. Přetěžování operátorů – těla metod Bod Bod::operator=( const Bod& b) { x = b.x; y = b.y; return *this; } Bod Bod::operator+( const Bod& b) { return Bod( x+b.x, y+b.y); } reference x  this->x aktualizace stavu kopie objektu (hodnotou přiřazení je přiřazovaná hodnota) vytvoření dočasného objektu konstruktor Bod::Bod(int, int) co to je ???

  19. Přetěžování operátorů - pravidla • Většinu operátorů jazyka C++ lze definovat pro uživatelské datové typy • Nelze předefinovat tyto operátory:.  .*  ::   ? :  sizeof • Alespoň jeden z operandů musí být třída nebo výčtový typ nebo reference na ně • Nelze předefinovat operace na číselných typech a ukazatelích • Předefinováním nelze měnit prioritu a asociativitu operátorů • Pro předefinované operátory nemusí platit identity definované pro základní typy • ++a nemusí být ekvivalentní a=a+1 • a[b] nemusí být ekvivalentní *(a+b)ani b[a] • je však velmi doporučeno dodržovat běžnou sémantiku • Pro předefinované operátory && a || neplatí pravidla o zkrácenémvyhodnocování • Typy skutečných operandů nemusejí přesně odpovídat typům formálních parametrů • stejná pravidla jako pro přetížené funkce

  20. Pozor!  Pro předefinované operátory nemusí platit identity definované pro základní typy: a=a+b  a+=b a[b]  *(a+b) Přetěžování operátorů – ekvivalence Bod Bod::operator+=( const Bod& b) { x += b.x; y += b.y; return *this; } Bod Bod::operator+=( const Bod& b) { return *this = *this + b; } this->operator=( this->operator+( b))

  21. copy konstruktor a operator= class Bod { private: int x, y; public: Bod( const Bod& b) { x=b.x; y=b.y; }; Bod operator=( const Bod& b) { x=b.x; y=b.y; return *this; }; }; Bod a(1,2); Bod k, m(a), n = a; k = m; není-li copy konstruktor nebo operator= definován, automaticky se vygeneruje copy konstruktor resp. operator= všech složek copy konstruktordefinice nového objektu operator= přiřazení do existujícího objektu Rozdíl mezi copy konstruktorem a přiřazením: • copy konstruktor se nemusí starat o předchozí stav objektu, přiřazení ano • přiřazení vrací (přiřazovanou) hodnotu, copy konstruktor nevrací nic

  22. class Bod { public: Bod( const Bod& b) { dosad(b); }; Bod operator=( const Bod& b) { dosad(b); return *this; }; int GetX() {return x; }; int GetY() {return y; }; void SetX(int x) { this->x = x; } void SetY(int y) { this->y = y; } private: int x, y; void dosad( const Bod & b) { x=b.x; y=b.y; }; }; Udržovatelnost kódu • Těla operátorů a konstruktorů • volání jiné funkce • Public část před private • Private položky nejsou zajímavé • Datové položky vždy private • Funkce Get a Set • Těla metod vždy v *.cpp souboru • Pro pozdější rozšíření • Lépe se hledá kde je implementované • Jména tříd • ToJeMojeTrida • Jména funkcí proměnných • mojePrvniFunkce

  23. Objekt a ukazatel na objekt class Zvire { ..... }; Zvire * pytlik; Zvire beruska; pytlik = &beruska; pytlik = new Zvire; C++ odlišuje objekt a ukazatel na něj • Rozdíl oproti jiným jazykům • Java, JavaScript, PHP, VisualBasic, ... • Analogie s chováním stuct v C • Ukazatel nelze použít dokud není splněna jedna z možností: • Přiřazen existující objekt • Reference • Dynamicky vytvořen nový objekt • Operátor new Nevzniká tu žádný objekt vzniká nový objekt

  24. Operátory new a delete new: alokace paměti, zavolání konstruktoru • není nutno testovat úspěšnost – mechanismus výjimek delete: zavolání destruktoru, dealokace paměti • jako parametr lze i 0 dynamická alokace, implicitní konstruktor náhrada za malloc() Bod a(1,2); Bod *pb = newBod; *pb = a + a; a = *pb; delete pb; pb = newBod( a); Bod *pc = newBod( 3, 5); a = *pb + *pc; delete pc; delete pb; uvolnění paměti další alokace, explicitní konstruktory alokace pole objektů char* buf = new char[10]; strcpy( buf, “ahoj”); ... delete[] buf; uvolnění paměti - u alokovaných polí nutno []

  25. Chytré řetězce – nápad Práce s řetězci v C + efektivní– nepohodlá a těžkopádná – časté chyby Chtěl bych: přiřazování, zřetězení, automatická alokace místa Str s1 = “ahoj”; Str s2 = “babi”; Str s3; s3 = s1 + ‘ ‘ + s2; s3 += “.”; s3 = (char*) malloc( strlen(s1) + strlen(s2) + 2); strcpy( s3, s1); s3[ strlen(s1)] = ‘ ‘; strcpy( s3 + strlen(s1) + 1, s2); ‘obyčejné‘ zřetězení – nechci se starat o to, kde sebrat místo

  26. Chytré řetězce - třída operace s řetězci ukazatel na alokovaná data class Str { private: char* buf; public: Str() { buf = 0; }; Str( const Str& s); Str( const char* s); ~Str() { delete[] buf; }; Str& operator=( const Str& s); Str operator+( const Str& s); int len() const{ return buf ? strlen(buf) : 0; }; }; implicitní konstruktor prázdný řetězec destruktor objekt si musí po sobě uklidit delete přežije i 0, nemusím testovat další metody (délka řetězce) Konstantní funkce, nemodifikuje objekt

  27. Destruktory ukazatel na alokovaná data class Str { private: char* buf; public: Str() { buf = 0; }; Str( const char* s) { buf = new char[ strlen( s) + 1]; strcpy( buf, s); }; ~Str() { delete[] buf; }; }; alokace paměti pro řetězec destruktor - automaticky se volá při zrušení objektu nemá argumenty nic nevrací

  28. Vyvolání destruktoru v kostruktoru s1 se alokuje paměť pro řetězec fce() { Str s1 = “ahoj”; Str* s2 = new Str( “babi”); ..... delete s2; ..... } dynamická alokace sp2 delete zavolá destruktor(a potom uvolní paměť) zde končí život s1 automaticky se vyvolá destruktor

  29. Řetězce – implementace uklidit po předchozím řetězci Str& Str::operator=( const Str& s) { delete[] buf; if( ! s.len()) { buf = 0; } else { buf = new char[ s.len()+1]; strcpy( buf, s.buf); } return *this; } Str::Str( const Str& s) { .... } prázdný řetězec alokace paměti okopírování znaků přiřazená hodnota – objekt sám reference kvůli efektivitě copy konstruktor – totéž bez delete a return

  30. později si ukážeme ještě lepší – counted pointers class Str { private: char* buf; void copy( const char* s); public: Str() { buf = 0; }; Str( const Str& s) { copy( s.buf); }; Str( const char* s) { copy( s); }; ~Str() { clear(); }; Str& operator=( const Str& s) { clear(); copy( s.buf); return *this; }; Str& operator=( const char* s) { clear(); copy( s); return *this; }; void clear() { delete[] buf; }; }; privátní metoda – alokace a kopírování konstruktory: jen alokace a kopírování přiřazení: i uklizení a návratová hodnota často potřebujeme uklízet O něco lepší implementace !!! buf = 0; nebo private !!!

  31. Implementace kopírování class Str { private: char* buf; void copy( const char* s); public: Str() { buf = 0; }; Str( const Str& s) { copy( s.buf); }; Str( const char* s) { copy( s); }; ~Str() { clear(); }; Str& operator=( const Str& s) { clear(); copy( s.buf); return *this; }; Str& operator=( const char* s) { clear(); copy( s); return *this; }; void clear() { delete[] buf; }; }; předpokládáme prázdný buf zařídí volající metoda - copy je private void Str::copy( const char* s) { if( !s || !*s) { buf = 0; } else { buf = new char[ strlen( s)+1]; if( buf) strcpy( buf, s); } } zkontrolovat prázdný řetězec alokace a kopírování Jde clear() přidat do copy() ???

  32. Zřetězení Str Str::operator+( const Str& s) { Str newstr; newstr.buf = new char[ len() + s.len() + 1]; strcpy( newstr.buf, buf); strcat( newstr.buf, s.buf); return newstr; } Str Str::operator+( const char* s) { Str newstr; newstr.buf = new char[ len() + strlen(s) + 1]; strcpy( newstr.buf, buf); strcat( newstr.buf, s); return newstr; } nový prázdný řetězec místo na znaky první operand druhý operand návratová hodnota nelze návrat reference (lokální dočasný objekt) nové hodnoty VŽDY vracet hodnotou

  33. Připojení řetězce když už umíme + a = proč si neudělat += class Str { .... public: .... Str& operator=( const Str& s); Str& operator=( const char* s); Str operator+( const Str& s); Str operator+( const char* s); Str& operator+=( const Str& s) { *this = *this + s; return *this; }; Str& operator+=( const char* s) { *this = *this + s; return *this; }; }; operator+( Str&) operator+( char*) lze vracet referencí existující hodnota

  34. Str a jednotlivé znaky class Str { ... public: Str(); Str( const Str&); Str( const char*); Str( char c) { buf = new char[ 2]; buf[0] = c; buf[1] = '\0'; }; Str& operator=( const Str&); Str& operator=( const char*); Str& operator=( char); Str operator+( int); Str operator+=( int); }; dodefinovat konstruktor, přiřazení a operace pro další typ

  35. Výstup později si ukážeme elegantnější řešení - streams neprázdný obsah na stdout class Str { ... public: int print() const { return buf ? printf( "%s", buf) : 0; }; }; Str s1 = "ahoj", s2("babi"), s3; s3 = s1 + ' ' + s2; s3.print(); Str("\n").print(); (s3 += ".\n").print(); ‘normálně’ spojím řetězce s mezerou ... a vytisknu dočasný objekt reference na s3

  36. ... and together class Str { private: char* buf; void copy( const char* s); void clear(); public: Str() { buf = 0; }; Str( const Str& s); Str( const char* s); Str( char c); ~Str(); Str& operator=( const Str& s); Str& operator=( const char* s); Str& operator=( char c); Str operator+( const Str& s); Str operator+( const char* s); Str operator+( char c); Str& operator+=( const Str& s); Str& operator+=( const char* s); Str& operator+=( char c); int len() const; int print() const; };

  37. vztah tříd předek-potomek – hierarchie přesnější názvosloví: základní (base) / odvozená třída (derived class) vícenásobná dědičnost dvakrát měř, jednou řež, protokoly specializace potomek má/umí něco navíc reusabilita jiné chování bez změny původní třídy Dědičnost Zvíře Pes Pitbul Člověk jez, vyměšuj sedni, lehni trhej uč_se

  38. pes jako potomek zvířete - definice class Zvire { protected: int zaludek; public: Zvire(); Zvire( int jidlo); int zije(); int jez( int jidlo); int vymesuj( int objem); }; potomek (odvozená třída od) Zvířete class Pes : public Zvire { private: enum t_stav { Stoji, Sedi, Lezi }; t_stav stav; public: Pes() { stav = Stoji; }; void sedni() { stav = Sedi; }; t_stav codela() { return stav; } }; přidaná položka položky předka metody předka Zvire pytlik; Pes azor; pytlik.jez(); azor.jez(); azor.sedni(); stav žaludek položky potomka metody potomka potomek obsahuje všechny položky a metody předka Přístup pro třídu a potomky jez, vyměšuj sedni

  39. Konstruktor a destruktor předka implicitní konstruktor předka (automaticky) class Zvire { ... ~Zvire() { printf( "zabijim zvire "); }; }; class Pes : public Zvire { ... public: Pes() { stav = Stoji; }; Pes( int jidlo) : Zvire( jidlo) { stav = Stoji; }; ~Pes() { printf( "zabijim psa "); }; }; { Pes azor; ... } explicitní konstruktor předka konstruktory předků a vložených tříd se volají před konstruktorem potomka destruktor předka se vyvolá automaticky po ukončení destruktoru potomka zabijim psa zabijim zvire

  40. Potomka lze přiřadit do předka (platí i pro ukazatele) Předka NELZE přiřadit do potomka (platí i pro ukazatele) Kompatibilita předka a potomka pes umí jíst, brouk neumí štěkat Zvire pytlik, *pz; Pes azor, *pp; pytlik = azor; pz = &azor; stav žaludek žaludek stav azor = pytlik; pp = &pytlik; žaludek žaludek nelze azor pytlik azor pytlik ???

  41. odlišné chování potomků – pozdní vazba (late binding) Polymorfismus Zvíře Pes Pitbul sní maso sní hodně masa najde něco v přírodě Člověk jde do restaurace jez jez jez jez

  42. Polymorfismus - motivace Tohle není polymorfismus ! class Zvire { jez() { priroda(); }; }; class Pes : public Zvire { jez() { maso(1); }; }; class Pitbul : public Pes { jez() { maso(10); }; }; class Clovek : public Zvire { jez() { hospoda(); }; }; Zvire pytlik; Pes punta; Pitbul zorro; Clovek pepa; pytlik.jez(); // priroda(); punta.jez(); // maso(1); zorro.jez(); // maso(10); pepa.jez(); // hospoda(); 'normální' vlastnost tříd zakrývání metod Při překladu je známo ze které třídy se volá metoda Každá třída má vlastní implementaci (tělo) metody jez

  43. Polymorfismus – takto nelze do ukazatele na základní třídu (předka) dám ukazatel na nově vytvořený objekt odvozené třídy (potomka) z je ukazatel na zvíře volá se Zvire::jez() Zvire* z; z = new Pes; z->jez(); // priroda(); z = new Clovek; z->jez(); // priroda(); Zvire* z; z = new Pes; z->Pes::jez(); // priroda(); z = new Clovek; z->Clovek::jez(); // priroda(); pokus – 'na tvrdo' chci metodu potomka nelze - syntaktická chyba pes není předkem zvířete

  44. Polymorfismus – takto bych to chtěl chtěl bych, aby se volaly 'správné' metody Zvire* z; z = new Pes; z->jez(); // maso(1); z = new Clovek; z->jez(); // hospoda(); Zvire* naseRodina[3]; naseRodina[0] = new Clovek; naseRodina[1] = new Pes; naseRodina[2] = new Pitbul; for( int i = 0; i < 3; i++) naseRodina[i]->jez(); Chci pokaždé se zavolat jinou metodu Rozlišení metody se musí dít za běhu

  45. Virtuální funkce - deklarace magické klíčové slovo virtual class Zvire { virtual jez() { priroda(); }; }; class Pes : public Zvire { virtual jez() { maso(1); }; }; class Pitbul : public Pes { virtual jez() { maso(10); }; }; class Clovek : public Zvire { virtual jez() { hospoda(); }; }; každý objekt si s sebou nese informaci kterou virtuální funkci používá

  46. Virtuální funkce - implementace stav žaludek žaludek tabulka virtuálních funkcí jez jez Zvire::jez() { priroda(); }; Pes::jez() { maso(1); }; zavolá se správná metoda podle tabulky virtuálních funkcí Zvire * z; z = new Zvire; z = new Pes; Pes Zvire z->jez();

  47. Virtuální funkce a konstruktory a destruktory v konstruktoru a destruktoru se vždy volá metoda vytvářeného/rušeného objektu nejdřív se zavolá konstruktor předka class A { public: virtual f(); A() { f(); }; // A::f ~A() { f(); }; // A::f g() { f(); }; // A/B::f }; class B : public A { public: virtual f(); B() { f(); }; // A::A B::f ~B() { f(); }; // B::f A::~A g() { f(); }; // B::f }; určí se za běhu podle skutečného typu objektu nejdřív se provede kód destruktoru, pak se zavolá destruktor předka

  48. Volání virtuálních funkcí class A { public: virtual f(); }; class B : public A { public: virtual f(); }; A a; // A::A B b; // B::B A * paa = &a; A * pab = &b; B * pbb = &b; // B * pba = &a; nelze!! (předka do potomka) a.f(); // A::f b.f(); // B::f paa->f(); // A::f pab->f(); // B::f pbb->f(); // B::f b.A::f(); // A::f b.B::f(); // B::f a.B::f(); // NE! paa->A::f(); // A::f pab->A::f(); // A::f pab->B::f(); // NE! pbb->A::f(); // A::f pbb->B::f(); // B::f pozdní vazba A::f kvalifikované volání B::f paa a pab b pbb

  49. Abstraktní třída, čistě virtuální funkce abstraktní třída nelze vytvořit objekt společný předek int armada; class Vojak { public: enum THod { vojin, desatnik, porucik, general }; Vojak( THod hod = vojin) { hodnost=hod; armada++; }; virtual void pal() = 0; virtual ~Vojak() { armada--; }; private: THod hodnost; }; pure virtual function ⇒abstraktní třída společné rozhraní class Samopal {}; class Kalasnikov : public Samopal {}; class Pesak : public Vojak { private: Samopal* sam; public: Pesak( THod hod=vojin) : Vojak( hod) { sam = new Kalasnikov; }; virtual void pal() { sam->pal(); }; virtual ~Pesak() { delete sam; }; }; POZOR!!! Nutný virtuální destruktor

  50. Abstraktní třídy, virtuální destruktory pokud by ~Vojak nebyl virtuální class Vojak { virtual ~Vojak() { armada--; }; }; class Pesak : public Vojak { virtual ~Pesak() { delete sam; }; }; // Vojak v; // NELZE – abstraktní třída Pesak p; // OK – Pesak Vojin Pesak* pp = new Pesak; // OK pp->pal(); // Pesak::pal Vojak* pv = new Pesak; // OK pv->pal(); // Pesak::pal delete pp; // OK, Pesak::~Pesak delete pv; // !!! Vojak::~Vojak POZOR!!! nejsou-li destruktory virtuální, nezruší se samopal Řešení: virtuální destruktor

More Related