Prezentace se nahrává, počkejte prosím

Prezentace se nahrává, počkejte prosím

Objektově orientované programování RNDr. Jan Lánský, Ph.D. Katedra softwarového inženýrství MFF UK Katedra informatiky VŠFS (Autor původní verze slajdů:

Podobné prezentace


Prezentace na téma: "Objektově orientované programování RNDr. Jan Lánský, Ph.D. Katedra softwarového inženýrství MFF UK Katedra informatiky VŠFS (Autor původní verze slajdů:"— Transkript prezentace:

1 Objektově orientované programování 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)

2 Zápočet Zdrojové kódy v minimálním rozsahu 1500 řádků, doporučeno 2500 řádků. Nejlépe: Vlastní ucelený příklad, který něco rozumného dělá Vlastní jednoduché příklady, zadání může být z knihy Nejhůře: Příklady probírané na cvičení PS: Aktivní účast na cvičeních (8 účastí z 12 možných) KS: Aktivní účast na soustředěních, 4 účasti z 9 možných) Chybějící účast (pod stanovený limit) lze nahradit : PS: +200 řádků zdrojových kódů za 1 hodinu neúčasti KS: +400 řádků zdrojových kódů za 1 neúčast na soustředění. Pozor v jeden den bývají obvykle 2 až 3 soustředění) Příklad: PS student X se zúčastnil jedné hodiny cvičení. Na zápočet odevzdá (8 – 1)*200 = 2900 řádků zdrojových kódů. Příklad: KS student Y se zúčastnil pouze výuky kdy proběhly 2 hodiny soustředění. Na zápočet odevzdá (4 – 2)*400 = 2300 řádků zdrojového kódu.

3 Zkouška Diskuze nad zdrojovými kódy předloženými k získání zápočtu. Zkušební okruhy Reference jako parametr funkce a návratová hodnota Přetěžování funkcí a operátorů, new a delete Bezparametrický konstruktor, copy konstruktor, operator =, destruktor Dědičnost, Virtuální funkce, abstraktní třídy Prostory jmen Streamy, práce se soubory Šablony funkcí a tříd STL kontejnery, iterátory a algoritmy Výjimky

4 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)

5 ZS (PJC) LS (OOP) Obsah předmětu CC 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...

6 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í Paradigmata programování side effects, údržba nelze rozumně rozšiřovat dále – generické programování šablony, STL

7 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

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

9 Třída zvíře - implementace # 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; } class Zvire { private: int zaludek; public: Zvire() {... }; int zije() {... }; int jez( int jidlo); int vymesuj( int objem); }; zvire.h zvire.cpp Třída metody :: operátor kvalifikace Implementace (tělo) metody Přístup k datům metody Středník !!!

10 Třída zvíře - použití #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  class Zvire { private: int zaludek; public: Zvire() {... }; int zije() {... }; int jez( int jidlo); int vymesuj( int objem); }; zvire.h mujprogram.cpp Import rozhraní Automatický konstruktor Instance třídy = objekt zaludek = 1 zaludek = 6 zaludek = 3

11 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);..... dvě instance třídy 6zaludek 2 pytlik: beruska: ? Metoda třídy - ke kterému objektu má přistupovat?

12 int Zvire::jez( int jidlo) { if( ! zije()) return 0; return zaludek += jidlo; }..... Zvire pytlik, beruska; pytlik.jez( 5); beruska.jez( 1);..... 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); zaludek2 pytlik:beruska: this C C++ Každá metoda dostane ' tajný ' parametr this – ukazatel na objekt zvire:: znamena zvire * this this->zije() this->zaludek

13 Reference int x = 1, y = 2; int *px; px = &x; *px = 3; int &ry = y; ry = 4; return *px + ry; reference pouze inicializace nelze měnit 3 x: :px 4 y: :ry 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 x::a 2 y::b zpřehlednění kódu přetěžování funkcí

14 Přetěžování funkcí 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 je definována svým identifikátorem a počtem a typem parametrů Funkce se stejným identifikátorem ale různým počtem parametrů Správná funkce podle počtu a typů skutečných parametrů Funkce se stejným počtem ale různým typem parametrů

15 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í Volá se stále stejná funkce int fce( int, int, int) 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)

16 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 Konstruktor s parametry Implicitní konstruktor bez parametrů Copy konstruktor X (const X&) vytvoří objekt jako kopii jiného

17 Konstruktor s parametry class Clovek { private: char jmeno[50]; Clovek(); public: //Clovek() { jmeno[0] = 0; }; Clovek( char * jmeno) { strcpy(this->jmeno, jmeno); }; Clovek honza( " Honza " ); //Clovek petr; Tímto zakážeme deklarovat objekt bez použití NEimplicitního konstruktoru Nejde, zakázali jsme 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

18 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ěžování operátorů - deklarace implicitní parametry  implicitní konstruktor přetížení operátoru + a + b  a.operator+(b) a = b  a.operator=(b) c.operator=(a.operator+(b)); c.assign( a.add( b)); Bod::Bod(0,0);

19 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); } Přetěžování operátorů – těla metod co to je ??? vytvoření dočasného objektu konstruktor Bod::Bod(int, int) x  this->x kopie objektu (hodnotou přiřazení je přiřazovaná hodnota) aktualizace stavu reference

20 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ém vyhodnocová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

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

22 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; copy konstruktor a operator= copy konstruktor definice nového objektu operator= přiřazení do existujícího objektu není-li copy konstruktor nebo operator= definován, automaticky se vygeneruje copy konstruktor resp. operator= všech složek 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

23 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 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; }; };

24 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 Objekt a ukazatel na objekt Nevzniká tu žádný objekt vzniká nový objekt

25 Bod a(1,2); Bod *pb = new Bod; *pb = a + a; a = *pb; delete pb; pb = new Bod( a); Bod *pc = new Bod( 3, 5); a = *pb + *pc; delete pc; delete pb; Operátory new a delete dynamická alokace, implicitní konstruktor náhrada za malloc() uvolnění paměti další alokace, explicitní konstruktory char* buf = new char[10]; strcpy( buf, “ahoj”);... delete[] buf; alokace pole objektů uvolnění paměti - u alokovaných polí nutno [] 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

26 Chytré řetězce – nápad Str s1 = “ahoj”; Str s2 = “babi”; Str s3; s3 = s1 + ‘ ‘ + s2; s3 += “.”; ‘ obyčejné ‘ zřetězení – nechci se starat o to, kde sebrat místo 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 s3 = (char*) malloc( strlen(s1) + strlen(s2) + 2); strcpy( s3, s1); s3[ strlen(s1)] = ‘ ‘; strcpy( s3 + strlen(s1) + 1, s2);

27 Chytré řetězce - třída 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; }; }; ukazatel na alokovaná data implicitní konstruktor prázdný řetězec destruktor objekt si musí po sobě uklidit delete přežije i 0, nemusím testovat operace s řetězci další metody (délka řetězce) Konstantní funkce, nemodifikuje objekt

28 Destruktory 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; }; }; ukazatel na alokovaná data alokace paměti pro řetězec destruktor - automaticky se volá při zrušení objektu nemá argumenty nic nevrací

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

30 Řetězce – implementace 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) {.... } uklidit po předchozím řetězci prázdný řetězec copy konstruktor – totéž bez delete a return alokace paměti okopírování znaků přiřazená hodnota – objekt sám reference kvůli efektivitě

31 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; }; }; O něco lepší implementace privátní metoda – alokace a kopírování často potřebujeme uklízet později si ukážeme ještě lepší – counted pointers konstruktory: jen alokace a kopírování přiřazení: i uklizení a návratová hodnota !!! buf = 0; nebo private !!!

32 void Str::copy( const char* s) { if( !s || !*s) { buf = 0; } else { buf = new char[ strlen( s)+1]; if( buf) strcpy( buf, s); } Implementace kopírování zkontrolovat prázdný řetězec alokace a kopírování předpokládáme prázdný buf zařídí volající metoda - copy je private 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; }; }; Jde clear() přidat do copy() ???

33 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; } Zřetězení nový prázdný řetězec místo na znaky první operand druhý operandnávratová hodnota nelze návrat reference (lokální dočasný objekt) nové hodnoty VŽDY vracet hodnotou

34 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; }; }; Připojení řetězce když už umíme + a = proč si neudělat += operator+( Str&) operator+( char*) lze vracet referencí existující hodnota

35 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); }; Str a jednotlivé znaky dodefinovat konstruktor, přiřazení a operace pro další typ

36 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(); Výstup neprázdný obsah na stdout později si ukážeme elegantnější řešení - streams ‘normálně’ spojím řetězce s mezerou... a vytisknu dočasný objekt reference na s3

37 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();... and together 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; };

38 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ířePesPitbul Člověk jez, vyměšujsedni, lehnitrhej uč_se

39 pes jako potomek zvířete - definice 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; } }; potomek (odvozená třída od) Zvířete class Zvire { protected: int zaludek; public: Zvire(); Zvire( int jidlo); int zije(); int jez( int jidlo); int vymesuj( int objem); }; přidaná položka položky potomka jez, vyměšuj stav žaludek sedni položky předka metody předka metody potomka Zvire pytlik; Pes azor; pytlik.jez(); azor.jez(); azor.sedni(); potomek obsahuje všechny položky a metody předka Přístup pro třídu a potomky

40 Konstruktor a destruktor předka 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;... } destruktor předka se vyvolá automaticky po ukončení destruktoru potomka zabijim psa zabijim zvire implicitní konstruktor předka (automaticky) explicitní konstruktor předka konstruktory předků a vložených tříd se volají před konstruktorem potomka

41 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 azor Zvire pytlik, *pz; Pes azor, *pp; pytlik = azor; pz = &azor; azor = pytlik; pp = &pytlik; stav žaludek pytlik žaludek stav žaludek pytlik azor ??? nelze

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

43 Polymorfismus - motivace 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(); Tohle není polymorfismus ! '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

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

45 Polymorfismus – takto bych to chtěl 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 chtěl bych, aby se volaly 'správné' metody

46 Virtuální funkce - deklarace 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(); }; }; magické klíčové slovo virtual každý objekt si s sebou nese informaci kterou virtuální funkci používá

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

48 Virtuální funkce a konstruktory a destruktory 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 }; nejdřív se zavolá konstruktor předka nejdřív se provede kód destruktoru, pak se zavolá destruktor předka v konstruktoru a destruktoru se vždy volá metoda vytvářeného/rušeného objektu určí se za běhu podle skutečného typu objektu

49 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 b B::f paa A::f a pab pbb kvalifikované volání

50 Abstraktní třída, čistě virtuální funkce 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; }; 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; }; }; pure virtual function ⇒ abstraktní třída společné rozhraní POZOR!!! Nutný virtuální destruktor abstraktní třída nelze vytvořit objekt společný předek

51 Abstraktní třídy, virtuální destruktory // 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 class Vojak { virtual ~Vojak() { armada--; }; }; class Pesak : public Vojak { virtual ~Pesak() { delete sam; }; }; POZOR!!! nejsou-li destruktory virtuální, nezruší se samopal Řešení: virtuální destruktor pokud by ~Vojak nebyl virtuální

52 Nesprávné užití dědičnosti Letadlo není potomkem svého motoru Důkaz: Co když má dva motory... Násobná dědičnost? Ne: Je třeba je odlišit Jezevčík umí vyhnat lišku z nory... Myslivec s jezevčíkem tedy také... Myslivec není potomkem svého jezevčíka Důkaz: Nežere granule... Kompozice? Ne: Nerodí se zároveň Mlok není potomkem ryby a savce Důkaz: Nemá dvě hlavy... Virtuální dědičnost? Ne: Nekojí Tlačítko není potomkem obdélníku a textu Kompozice Skládání velkých objektů z malých C++: Třída s datovými položkami Delegace Převedení funkčnosti na jiný objekt C++: Ukazatel Společný abstraktní předek Obratlovec Vizuální objekt

53 Prostory jmen (namespaces) namespace aa { int p; int f1( int x) { return x + p; } int f2( int x, int y); } int aa::f2( int x, int y) { return p * (x + y); } aa::f1( aa::f2( 5, 6)); zapouzdření identifikátorů prevence kolizí (velké projekty, knihovny) stejné identifikátory v různých prostorech jmen definice prostoru jmen přístup k identifikátoru ze stejného prostoru definice funkce mimo prostor jmen přístup k identifikátorům přes ::

54 using namespace std; namespace aa { int p; int q; } int g( int n) { cout << (n + aa::p); } namespace aa { int f3( int x) { return 1 + ::g( x); } Prostory jmen prostor jmen se může opakovaně otevírat a zavírat explicitní přístup ke globálnímu identifikátoru ::id standardní knihovny – namespace std rozbalení std znovuotevření prostoru aa přístup do aa přístup ke globálnímu identifikátoru přístup k identifikátorům std

55 Prostory jmen a standardní knihovny stará konvence: stdio.h, ctype.h, iostream.h identifikátory v globálním prostoru jmen strlen, FILE nová konvence: cstdio, cctype, iostream identifikátory uzavřené do namespace std std::strlen, std::FILE standardní knihovny C++ Základní knihovny z C přejmenované podle nové konvence Rozšířené C++ knihovny iostream: znakový formátovaný vstup a výstup STL: Standard Template Library použití šablon kontejnery, iterátory, algoritmy

56 #include using namespace std; int main() { int n; cout << "Rekni cislo: "; cin >> n; cout << "Mam vic: " << (n+1) << ", hec!" << endl; } Vstup a výstup - proudy (streams) hierarchie tříd pro (formátovaný znakový) vstup a výstup jednotné rozhraní pro v/v do souborů a paměti,... operátory >, manipulátory motivace: rozšiřitelnost bezpečnost definice základních tříd a manipulátorů ostream cout  FILE* stdout istream cin  FILE* stdin ostream& ostream::operator<< () istream& istream::operator>> () datum d( 12, 3, 2004); printf( "dnes je %?", d); int i; printf( "Jmenuji se %s", i);

57 Streams – hierarchie tříd

58 – základní operace, standardní v/v, manipulátory bez parametrů cin, cout, >, endl, ws,... – manipulátory s parametry setw, setfill,... – vstup a výstup do souborů fstream, ifstream, ofstream,... - vstup a výstup do paměti (chytré řetězce) strstream, istrstream, ostrstream,... Hlavičkové soubory

59 do proudu lze vkládat manipulátory – změní stav proudu endlpošle buffer na výstup a odřádkuje left, rightzarovnávej doleva / doprava dec, hexv desítkové / šestnáctkové soustavě wspřeskoč bílé znaky (na vstupu) setw(int)šířka výstupního pole (jen pro následující číselnou položku) setfill(int)výplňkový znak... a spousty dalších [...17] cout << "[" << setfill('.') << setw(5) << 17 << "]" << endl; Manipulátory nastaví výplňový znak nastaví šíři výstupu vytiskne podle aktuálního nastavení výstup

60 #include using namespace std; int main() { fstream f( "C:\\src\\pokus.txt", ios::out); if( ! f) error(); f << "bubu" << endl; } Výstup do souboru spojení proudu se souborem v konstruktoru způsob otevření ios::in, out, app, trunc, binary,... Př: ios::in | ios::binary operator ! (ostream&) vrátí true když se operace nepodařila soubory není třeba zavírat, zavře je automaticky destruktor třída pro souborový proud

61 Další metody vstupních proudů Vstup get( kam, délka, koncový_znak) getline( kam, délka, koncový_znak) ignore( délka, koncový_znak) read( pole_znaků, délka) tellg() seekg( posun, odkud) unget() pro binární vstup a výstup nelze použít operátory > Výstup put( znak) write( pole_znaků, délka) tellp() seekp(posun, odkud) flush()... a další int i = 17; ofstream f( "pokus.txt", ios::binary); if( ! f) error(); f.write( (char*)&i, sizeof( i)); pole bajtů a jejich počet

62 Spřátelené funkce – vlastní výstup class Complx { private: int re, im; public: Complx( int _re = 0, int _im = 0) { re = _re; im = _im; }; friend ostream& operator<<( ostream& s, Complx& c) { return s << c.re << "+" << c.im << "i"; }; }; Complx x(1,2); cout << x << endl; spřátelená (friend) funkce může přistupovat k privátním položkám POZOR! Toto není metoda třídy!

63 množina funkcí/tříd lišících se pouze typem parametrů/položek vzor, podle kterého překladač vytvoří funkci nebo třídu (instanci) pro konkrétní typ template T max( T a, T b) { return a > b ? a : b; }; int x = 10, y = 20; double m = 1.1, n = 2.2; cout << max(x,y) << max(m,n) << endl; Šablony Definice šablony funkce Typový parametr T nahrazuje skutečný typ místo typename lze class int max( int a, int b)double max( double a, double b)

64 template class Guma { private: int size; T* array; public: const int default_size = 10; Guma( int _size = default_size) { size = _size; array = new T[size]; }; ~Guma() { delete array; } T& operator[] (int n); }; int main() { Guma ip(5); ip[3] = 999; Šablony tříd - definice přetížený operator[] size: 5 array: ? 3 ???? 4210 instance šablony třídy definice proměnné pole neznámého typu

65 template class Guma { private: int size; T* array; public: T& operator[] (int n); }; template T& Guma ::operator[] (int n) { if( n >= size) { T* na = new T[ n + 1]; for( int i = 0; i < size; i++) na[i] = array[i]; delete array; array = na; size = n + 1; } return array[n]; } Šablony metod, instance šablon struct Krabice { int a, b; char jm[10]; }; typedef Guma polekrab; int main(int argc, char* argv[]) { Guma ip(5); polekrab pk; ip[3] = 999; pk[12].a = ip[3]; instance šablony třídy definice typu pk[i] je typu Krabice& definice šablony metody

66 list sez; sez.push_front( 1); sez.push_back( 2); sez.push_front( 3); list ::iterator i; for( i = sez.begin(); i != sez.end(); i++) cout << "[" << * i << "]"; STL – Standard Template Library obousměrný seznam kontejnery – datové struktury pro ukládání dat a manipulaci s nimi iterátory – třídy pro přístup k datům kontejnerů algoritmy – základní algoritmy nad kontejnery (třídění, procházení, hledání) další pomocné třídy – alokátory, komparátory, funktory... přidání prvku zepředu... zezadu... zepředu iterátor seznamu průchod seznamem přístup k datům přes iterátor – operator*

67 STL – kontejnery Sekvenční kontejneryAsociativní kontejnery uspořádanésetříděné

68 STL – kontejnery Sekvenční kontejnery deque – dvoustranná fronta [dek] umožňuje v konst. čase přidávat na začátek i konec implementace typicky pomocí polí adaptéry (specializované použití i rozhraní): stack, queue, priority_queue vector – pole (gumové) přístup k prvku v konstantním čase jako vector se chová i string a standardní pole (T x[]) string – chytré řetězce =, +, += a mnoho dalších operací a metod list – dvousměrný seznam implementace: spojový seznam umožňuje v konstantním čase přidávat prvky na libovolné místo Asociativní kontejnery map, multimap – zobrazení, asociativní pole, slovník, mapa uspořádaná struktura indexovaná libovolným typem, pair: klíč, hodnota) set, multiset – množina, multimnožina každý prvek nejvýše jednou / vícekrát

69 STL – iterátory a metody kontejnerů kontejner ::iteratoriterátor příslušného kontejneru T& iterator::operator*přístup k prvku přes iterátor begin(), end()iterátor na začátek / za(!) konec kontejneru push_front(), push_back()přidání prvku na začátek / konec pop_front(), pop_back()odebrání prvku ze začátku / konce – nevrací hodnotu! front(), back()prvek na začátku / konci operator[], at()přímý přístup k prvku insert(iterator,T)vložení prvku na místo určené iterátorem size(), empty(), clear()velikost / neprázdost / smazání kontejneru push(), pop(), top() přidání / odebrání / prvek na vrcholu zásobníku

70 STL – použití iterátorů vector pole; vector ::iterator p; for( p = pole.begin(); p != pole.end(); p++) cout << "[" << *p << "]"; vytvoření celočíselného vectoru pole p je iterátor do vector pole.begin() vrátí iterátor na začátek pole pole.end() vrátí iterátor za konec pole p++ (overl.) zařídí, že p bude ukazovat na další prvek jestli p už nedosáhl konce *p (overl.) vrátí hodnotu prvku na nějž ukazuje iterátor

71 map ts; ts["Filip"] = " "; ts["Petra"] = " "; ts["David"] = " "; ts["Kuba"] = " "; cout << "Telefon Petry: " << ts["Petra"] << endl; map ::iterator ti; for( ti = ts.begin(); ti != ts.end(); ti++) cout first second << endl; STL – použití asociativního pole pair iterator::operator* ti->first ≡ (*ti).first ts: pair: string second string first operator [] (const string&) vyhledání podle first

72 STL – algoritmy Inicializace fillFills a sequence with an initial value fill_nFills n positions with an initial value copyCopies a sequence into another sequence copy_backward Copies a sequence into another sequence generateInitializes a sequence using a generator generate_nInitializes n positions using a generator swap_ranges Swaps values from two parallel sequences Vyhledávání findFinds an element matching the argument find_ifFinds an element satisfying a condition adjacent_find Finds consecutive duplicate elements find_first_ofFinds one member of a seq. in another seq. find_endFinds the last occurr. of a sub-seq. in a seq. searchMatches a sub-sequence within a sequence max_element Finds the maximum value in a sequence min_element Finds the minimum value in a sequence mismatchFinds first mismatch in parallel sequences Mazání removeRemoves elements that match condition unique Removes all but first of duplicate values Ostatní for_eachApplies a function to each element Transformace prvků reverseReverses the elements in a sequence replaceReplaces specific values with new value replace_ifReplaces elements matching predicate rotateRotates elements in a sequence around a point next_permutation Generates permutations in sequence prev_permutation Generates permutations in reverse seq. inplace_merge Merges two adjacent sequences into one random_shuffle Randomly rearranges elements in a seq. Třídění sortSorts all elements make_heapConverts a range into a heap Skalární výpočty countCounts number of elements matching value count_ifCounts elements matching predicate accumulateReduces sequence to a scalar value equalChecks two sequences for equality lexicographical_compare Compares two sequences Výpočty generující sekvence transformTransforms each element partial_sumGenerates sequence of partial sums adjacent_difference Gen. sequence of adjacent differences + mnoho dalších

73 STL – použití algoritmů void tiskni( int x) { cout << " [" << x << "]"; } vector pole; vector ::iterator b, e, p; generate( b = pole.begin(), e = pole.end(), rand); for_each( b, e, tiskni); p = max_element( b, e); sort( p, e); unique( p, e); for_each( b, pole.end(), tiskni); vlastní funkce pro jeden prvek pro každý prvek se zavolá funkce vyplní se náhodnými čísly odstraní duplicity setřídí část pole od max. prvku do konce najde max. prvek vrátí iterátor !! pozor – může zneplatnit iterátor e

74 STL – chybová hlášení \SRC\templ\templ.cpp(101) : error C2664: 'class std::_Tree,class std::allocator >,struct std::pair,class std::allocator > const,class std::basic_string,class std::allocator > >,struct std::map,class std::allocator >,class std::basic_string,class std::allocator >,struct std::less,class std::allocator > >,class std::allocator,class std::allocator > > >::_Kfn,struct std::less,class std::allocator > >,class std::allocator,class std::allocator > > >::iterator __thiscall std::map,class std::allocator >,class std::basic_string,class std::allocator >,struct std::less,class std::allocator > >,class std::allocator,class std::allocator > > >::insert(class std::_Tree,class std::allocator >,struct std::pair,class std::allocator > const,class std::basic_string,class std::allocator > >,struct std::map,class std::allocator >,class std::basic_string,class std::allocator >,struct std::less,class std::allocator > >,class std::allocator,class std::allocator > > >::_Kfn,struct std::less,class std::allocator > >,class std::allocator,class std::allocator > > >::iterator,const struct std::pair,class std::allocator > const,class std::basic_string,class std::allocator > > &)' : cannot convert parameter 1 from 'char [6]' to 'class std::_Tree,class std::allocator >,struct std::pair,class std::allocator > const,class std::basic_string,class std::allocator > >,struct std::map,class std::allocator >,class std::basic_string,class std::allocator >,struct std::less,class std::allocator > >,class std::allocator,class std::allocator > > >::_Kfn,struct std::less,class std::allocator > >,class std::allocator,class std::allocator > > >::iterator'

75 string Compare strings==,!=,,>=, compare() Concatenates strings+ Replaces charactersreplace() Changes the number of characters (deletes or appends chars at the end) resize() Removes all characters (makes it empty)clear() Deletes characterserase() Inserts charactersinsert() Append characters+=,append(), push_back() Swaps values between two stringsswap() Assign a new value=, assign() Destroys a stringdestructor Create or copy a stringconstructors Returns the allocatorget_allocator() Provide reverse iterator supportrbegin(), rend() Provide normal iterator supportbegin(), end() Search for a certain substring or characterfind functions Returns a certain substringsubstr() Returns the value as character arraydata() Returns the value as C-stringc_str() Copies or writes the contents to a C-stringcopy() Writes the value to a stream<< Read the value from a stream>>, getline() Access a character[], at() Returns the number of characters that can held without be reallocation capacity() Returns whether the string is emptyempty() Returns the maximum possible number of characters max_size() Return the number of characterssize(), length()

76 Výjimky Motivace: co dělat, když (knihovní) funkce zjistí chybu? nedostatek paměti, nelze otevřít soubor, nulový ukazatel,... Vypsat zprávu na 'obrazovku' a skončit FUJ! Nikdy! Nastavit do globální funkce příznak chyby problém s více vlákny, nutnost neustále testovat Vrátit 'divnou' hodnotu takhle funguje většina knihovních funkcí C nepříliš praktické, testování každé funkce, vnořené testy divná hodnota nemusí existovat Co chceme: oddělit detekci výjimečné situace od jejího zpracování po výskytu 'chyby' (výjimečné situace) automaticky skočit na zpracování kulturně po sobě uklidit (volání destruktorů) Řešení v C++: mechanismus výjimek

77 void mojefce( char* str) { if( ! str) throw runtime_error( "Nic!"); cout << str; } int main(int argc, char* argv[]) { char* p = 0; try { mojefce( p); } catch( runtime_error& e) { cout << "Chyba: " << e.what() << endl; } return 0; } Výjimky - jednoduchý příklad vyvolání výjimky typ výjimky standardní třída potomek exception pokusný blok try block handler(y) typ výjimky standardní metoda třídy runtime_error řetězec z konstruktoru

78 char* mojefce( long n) { char* bigbigbig = new char[n]; cout << "Proslo to" << endl; return bigbigbig; } int main(int argc, char* argv[]) { char* p = 0; try { mojefce( ); cout << "Vratil jsem se" << endl; } catch( runtime_error& e) { cout << "Chyba: " << e.what() << endl; } return 0; } Výjimky - jednoduchý příklad pokud se nepovede naalokovat, nastane výjimka při výjimce se dále nepokračuje, hledá se nejbližší volný handler nalezený handler

80 Specifikace výjimek funkcí Problém: jak programátor pozná které výjimky má ošetřovat? Řešení: funkce může specifikovat výjimky, které může vyvolat void mojefce( char* s) throw (runtime_error, mojechyba); int jinafce( void) throw(); char* tretifce( char* s); funkce může vyvolat výjimky těchto typů funkce nevyvolává žádnou výjimku funkce může vyvolat libovolnou výjimku

81 ... co jsme neprobrali spoustu věcí jazyk protected, volatile, static, operátory.* a ->*, ukazatele na funkce a metody,... vícenásobná dědičnost, protokoly RTTI, typeid, type_info static_cast, dynamic_cast, reinterpret_cast, const_cast podrobněji knihovny, zejména streams a STL, efektivní používání knihoven OOP counted pointers, mělké vs. hluboké kopie objektová paradigmata – zprávy, obálkové třídy, subtyping, forwarding hlouběji o objektovém návrhu, reusabilitě, efektivitě implementace funktory a jiné specialitky spoustu věcí kdo chcete C++ opravdu profesionálně používat, přečtěte si literaturu (Meyers, Sutter) nebuďte líní – zkoušejte i jednoduché věci naprogramovat 'profesionálně'

82 Dodelat na priste Vice slajdu o pretezovani operatoru (i unarni) Vice slajdu o referenci Chytre retezce – pocitane odkazy


Stáhnout ppt "Objektově orientované programování RNDr. Jan Lánský, Ph.D. Katedra softwarového inženýrství MFF UK Katedra informatiky VŠFS (Autor původní verze slajdů:"

Podobné prezentace


Reklamy Google