770 likes | 943 Views
Úvod do objektov ě orientovaného programování. RNDr. Filip Zavoral, Ph.D. Katedra softwarového inženýrství MFF UK Malostransk é nám. 25, Praha 1 Filip.Zavoral @ mff.cuni.cz http ://ulita.ms.mff.cuni.cz/~zavoral/VSFS. Studijní povinnosti. Přednáška 1.5 hod (od 9 :00)
E N D
Úvod do objektově orientovaného programování RNDr. Filip Zavoral, Ph.D. Katedra softwarového inženýrství MFF UK Malostranské nám. 25, Praha 1 Filip.Zavoral@mff.cuni.cz http://ulita.ms.mff.cuni.cz/~zavoral/VSFS
Studijní povinnosti • Přednáška 1.5 hod (od 9:00) • Cvičení 1.5 hod (do 11:30) • Následné osobní konzultace (45 min ve studovně) • Klasifikovaný zápočet • Příklady na cvičeních, 'domácíúkoly' • Závěrečný test • Odladit krátký program v omezeném čase • Podmínka účasti – úspěšně absolvovaný test z PJC (ZS) • Zápočtový program • Libovolný prográmek, zůstává ze ZS, stačí C, nemusí být OOP • Samostatně vypracovat během semestru
Literatura • Bjarne Stroustrup: The C++ Programming Language • Bruce Eckel: Myslíme v jazyku C++ • James O. Coplien: • Advanced C++ Programming Styles and Idioms • Herb Sutter: Exceptional C++ • Miroslav Virius: Pasti a propasti jazyka C++ • Miroslav Virius: Programování v C++ • http://kmdec.fjfi.cvut.cz/~virius/liter/litCpp.htm
Obsah předmětu • ZS (PJC) LS (OOP) C++ C++ C C
Obsah předmětu • Paradigmataprogramování, OOP • Objekty, zapouzdření, dědičnost • Konstruktory a destruktory, new a delete • Pozdní vazba, virtuální funkce • Přetěžování funkcí, předefinování operátorů • Abstraktní datové typy • OO styly a idiomy • Výjimky, šablony, knihovny, streams, STL, RTTI, ...
Paradigmata programování • Procedurální programování • jakou akci mám provést • vstup – výpočet (algoritmus) – výstup • black box: procedura / funkce • !! side effects, údržba • Modulární programování • rozdělení problému na komponenty • procedury pracují nad daty - rozhraní • black box: modul
Paradigmata programování • Datová abstrakce • vytvoření vlastního datového typu (abstract/user defined d.t.) • kompletní množina operací nad tímto typem • black box: datový typ • !! nelze rozumně rozšiřovat • Objektové programování • dědičnost – obecné / konkrétní vlastnosti • polymorfismus • možnost pozdějších rozšíření
Třídy a objekty • Koncepční pohled • objekt: entita reagující na vnější podněty • třída: monžina stejně reagujících entit • Technický pohled • objekt: struktura obsahující data a funkce, instance třídy • datové položky, metody; angl. members • třída: typ objektu • Rozhraní – veřejné informace pro uživatele • Implementace – (neveřejná) interní data a metody
Zvíře v C++ - rozhraní definice třídy zvire.h vnitřní stav (privátní) class zvire { private: int zaludek; public: zvire() {zaludek = 1; }; int zije() { return zaludek>0; }; int jez( int jidlo); int vymesuj( int objem); }; datová položka inline tělo funkce inline tělo funkce rozhraní (veřejné) konstruktor (inicializace) deklarace metody metody
Zvíře - implementace :: operátor kvalifikace zvire.cpp 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; } implementace (tělo) metody třída metody přístup k datům třídy
Zvíře - použití mujprogram.cpp #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; ..... } import rozhraní instance třídy = objekt zaludek = 1 automaticky konstruktor zaludek = 3 -2 0
Objekt - instance třídy Metoda třídy - ke kterému objektu má přistupovat? int zvire::jez( int jidlo) { if( ! zije()) return 0; return zaludek += jidlo; } ..... zvire pytlik, beruska; pytlik.jez( 5); beruska.jez( 1); ..... ? pytlik: beruska: dvě instance třídy
this Každá metoda dostane tajný parametr this - ukazatel na objekt C-style řešení intjez( zvire* this, int jidlo) { if( ! zije( this)) return 0; return this->zaludek += jidlo; } ..... zvire pytlik, beruska; jez( &pytlik, 5); jez( &beruska, 1); ..... this pytlik: beruska:
Reference x: 3 :px int x = 1, y = 2; int *px; px = &x; *px = 3; int &ry = y; ry = 4; return *px + ry; y: 4 :ry reference i ukazatele jsou reprezentovány adresou reference - pouze inicializacenelze měnit
Reference jako parametry skutečné parametry odkazy na proměnné swap( int& a, int& b) { int c = a; a = b; b = c; } int x = 1, y = 2; swap( x, y); x: 1 :a y: 2 :b význam: zpřehlednění kódu přetěžování operátorů !
Přetěžování funkcí • Funkce je definována svým identifikátorem a počtem a typem parametrů Funkce se stejným identifikátorem alerůzným počtem parametrů Funkce se stejným identifikátorem alerůzným počtem parametrů int pocitej( int x) { return x+1; } int pocitej( int a, int b) { return 2*a + b; } pocitej( 1); // int pocitej( int) pocitej( 1, 2); // int pocitej( int, int) Správná funkce podle počtu a typů skutečných parametrů
Konstruktory class zvire { private: int zaludek; public: zvire() {zaludek = 1; }; zvire( int zal) { zaludek = zal; }; zvire( zvire& vzor) { zaludek = vzor.zaludek; }; }; zvire beruska; zvire pytlik( 20); zvire beberuska( beruska); zvire tlustoch = pytlik; implicitní konstruktor copy (kopírovací) konstruktorX(X&) různé zápisy copy konstruktoru
Přetěžování operátorů - deklarace class bod { private: int x, y; public: bod( int xx=0, int yy=0) { x=xx; y=yy; }; bod operator+( bod&); bod operator=( bod&); }; bod a(1,2), b, c; c = a + b; implicitní parametry implicitní konstruktor 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));
Přetěžování operátorů – těla metod bod bod::operator=( bod& b) { x = b.x; y = b.y; return *this; } bod bod::operator+( 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) co to je ??? vytvoření dočasného objektu konstruktor bod::bod(int, int)
Přetěžování operátorů – ekvivalence • Pozor! Pro předefinované operátory nemusí platit identity definované pro základní typy: • a=a+b a+=b a[b] *(a+b) bod bod::operator+=( bod& b) { x += b.x; y += b.y; return *this; } bod bod::operator+=( bod& b) { return *this = *this + b; } this->operator=( this->operator+( b))
copy konstruktor a operator= class bod { private: int x, y; public: bod( bod& b) { x=b.x; y=b.y; }; bod operator=( 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 konstruktordeklarace nového objektu operator= přiřazení do existujícího obj.
Operátory new a delete 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; dynamická alokace, implicitní konstruktor náhrada za malloc() uvolnění paměti další alokace, explicitní konstruktory
new a delete – pole a reference char* buf = new char[10]; strcpy( buf, “ahoj”); ... delete[] buf; alokace pole objektů uvolnění paměti - u alokovaných polí nutno [] bod& rb = *new bod( 1,2); rb = rb + rb; delete &rb; dynamická inicializace reference
Chytré řetězce – nápad • Práce s řetězci v C • + efektivní– nepohodlá – chyby • Chtěl bych: přiřazování, spojování, alokace místa s3 = malloc( strlen(s1) + strlen(s2) + 2); strcpy( s3, s1); s3[strlen(s1)] = ‘ ‘; strcpy( s3 + strlen(s1) + 1, s2); 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
Chytré řetězce - třída operace s řetězci class str { private: char* buf; public: str() {buf = 0; }; str( str& s); str( char* s); ~str() {delete[] buf; }; str& operator=( str& s); str operator+( str& s); int len() { 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) další metody (délka řetězce)
Destruktory ukazatel na alokovaná data class str { private: char* buf; public: str() { buf = 0; }; str( 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í objekt po sobě musí uklidit
Vyvolání destruktoru v kostruktoru s1 se alokuje paměť 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
Řetězce – implementace uklidit po předchozím řetězci str& str::operator=( str& s) { delete[] buf; if( ! s.len()) { buf = 0; } else { buf = new char[ s.len()+1]; if( buf) strcpy( buf, s.buf); } return *this; } str::str( 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
O něco lepší implementace později si ukážeme ještě lepší – counted pointers class str { private: char* buf; void copy( char* s); public: str() {buf = 0; }; str( str& s) { copy( s.buf); }; str( char* s) { copy( s); }; ~str() { clear(); }; str& operator=( str& s){ clear(); copy( s.buf); return *this; }; str& operator=( char* s){ clear(); copy( s); return *this; }; void clear() { delete[] buf; }; }; privátní metoda – provádí vlastní alokaci a kopírování často potřebujeme uklízet
Implementace kopírování zkontrolovatprázdný řetězec void str::copy( char* s) { if( !s || !*s) { buf = 0; } else { buf = new char[ strlen( s)+1]; if( buf) strcpy( buf, s); } } předpokládáme prázdný buf zařídí volající metoda – copy je private alokace a kopírování
Zřetězení str str::operator+( 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+( char* s) { .... } nový prázdný řetězec místo na znaky první operand druhý operand návratová hodnota
Připojení řetězce class str { .... public: .... str& operator=( str& s); str& operator=( char* s); str operator+( str& s); str operator+( char* s); str& operator+=( str& s) { *this = *this + s; return *this; }; str& operator+=( char* s) { *this = *this + s; return *this; }; }; když už umíme + a = proč si neudělat += operator+( str&) operator+( char*)
Spolupráce str a int (jednotlivé znaky) class str { ... public: str(); str( str&); str( char*); str( int c){ buf = new char[ 2]; buf[0] = c; buf[1] = '\0'; }; str& operator=( str&); str& operator=( char*); str& operator=( int); str operator+( int); str operator+=( int); }; dodefinovat konstruktor, přiřazení a operace pro další typ dodefinovat konstruktor, přiřazení a operace pro další typ
později si ukážeme elegantnější řešení - streams Výstup neprázdný obsah na stdout class str { ... public: int print() { 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
... and together class str { private: char* buf; void copy( char* s); public: str() { buf = 0; }; str( str& s); str( char* s); str( int c); ~str(); str& operator=( str& s); str& operator=( char* s); str& operator=( int c); str operator+( str& s); str operator+( char* s); str operator+( int c); str& operator+=( str& s); str& operator+=( char* s); str& operator+=( int c); void clear(); int len(); int print(); };
Dědičnost • Vztah tříd předek-potomek – hierarchie • Vícenásobná dědičnost • specializace (potomek má/umí něco navíc) • reusabilita (jiné chování bez změny pův. třídy) protokoly - později zvíře pes pitbul jez, vyměšuj sedni, lehni trhej člověk uč_se
pes jako potomek zvířete - definice potomek zvířete class zvire { private: int zaludek; public: zvire(); zvire( int jidlo); int zije(); int jez( int jidlo); int vymesuj( int objem); }; class pes : public zvire { private: enum t_stav { Stoji, Sedi, Lezi }; t_stav stav; public: pes() { stav = Stoji; }; int sedni() { stav = Sedi; }; t_stav codela() { return stav; } };
pes jako potomek – obsah objektu metody předka položky předka stav žaludek jez, vyměšuj metody potomka položky potomka sedni zvire pytlik; pes azor; pytlik.jez(); azor.jez(); azor.sedni(); potomek obsahuje všechny položky a metody předka
Konstruktor předka implicitní konstruktor předka (automaticky) class pes : public zvire { ... public: pes() { stav = Stoji; }; pes( int jidlo) : zvire( jidlo) { stav = Stoji; }; }; konstruktory předků a vložených tříd se volají před konstruktorem potomka explicitní konstruktor předka
Destruktor předka class zvire { ... ~zvire() { printf( "zabijim zvire "); }; }; class pes : public zvire { ... ~pes() { printf( "zabijim psa "); }; }; { pes azor; ... } destruktor předkase vyvolá automaticky po ukončení destruktoru potomka zabijim psa zabijim zvire
Kompatibilita předka a potomka • Potomka lze přiřadit do předka (platí i pro ukazatele) • Předka NELZE přiřadit do potomka (platí i pro uk.) pes umí jíst, brouk neumí štěkat azor zvire pytlik, *pz; pes azor, *pp; pytlik = azor; *pz = &azor; pytlik stav žaludek žaludek azor pytlik azor = pytlik; *pp = &pytlik; stav žaludek žaludek ??? nelze
Polymorfismus • odlišné chování potomků – pozdní vazba (late binding) zvíře pes pitbul jez jez jez sní maso sní hodně masa najde něco v přírodě člověk jez jde do restaurace
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
Polymorfismus – takto nelze 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(); nelze (syntaktická chyba, pes není předkem zvířete)
Polymorfismus – takto bych to chtěl zvire* z; z = new pes; z->jez(); // maso(1); z = new clovek; z->jez(); // hospoda(); zvire* nase_rodina[3]; nase_rodina[0] = new clovek; nase_rodina[1] = new pes; nase_rodina[2] = new pitbul; for( int i = 0; i < 3; i++) z->jez(); Pokaždé se zavolá jiná metoda Rozlišení metody se děje za běhu Pokaždé se zavolá jiná metoda Rozlišení metody se děje za běhu
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á
Virtuální funkce - implementace zvire * z; z = new zvire; z = new pes; pes zvire stav žaludek žaludek tabulka virtuálních funkcí tabulka virtuálních funkcí jez jez zvire::jez() { priroda(); }; pes::jez() { maso(1); }; z->jez(); zavolá se správná metoda
Virtuální funkce a konstruktory 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 };
Volání virtuálních funkcí A a; // A::A B b; // B::B A * paa = &a; A * pab = &b; B * pbb = &b; 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 paa a A::f kvalifikované volání pab b B::f pbb
Abstraktní třída, čistě virtuální funkce int armada; class vojak { public:enum THod { vojin, desatnik, porucik, general }; private: THod hodnost; public: vojak( THod hod = vojin) { hodnost=hod; armada++; }; virtual void pal() = 0; ~vojak() { armada--; }; }; 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(); }; ~pesak() { delete sam; }; }; pure virtual function abstraktní třída POZOR!!! Nutný virtuální destruktor