Upload
errol
View
47
Download
1
Embed Size (px)
DESCRIPTION
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 ) [email protected] - PowerPoint PPT Presentation
Citation preview
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)
[email protected]://kocour.ms.mff.cuni.cz/~lansky/
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
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 STLHerb Sutter: Exceptional C++, More Exceptional C++Que: ANSI/ISO C++ Professional Programmer's HandbookBruce Eckel: Myslíme v jazyce C++James O. Coplien: Advanced C++ Programming Styles and IdiomsBjarne Stroustrup: The C++ Programming Language
ISO/IEC 14882, ANSI: Programming languages - C++ (1998, 2003)
ZS (PJC) LS (OOP)
Obsah předmětu
C C
C++
C++
Paradigmata programování, OOPObjekty, zapouzdření, dědičnost, konstruktory a destruktoryPřetěžování funkcí, předefinování operátorůPozdní vazba, virtuální funkceAbstraktní datové typyŠablony, výjimky, prostory jmen.Objektové knihovny: streams, STLRTTI, OO styly a idiomy...
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
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živateleImplementace – (neveřejná) interní data a metody (funkce)
Třídy a objekty
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
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.hzvire.cpp
Třída metody
:: operátor kvalifikace
Implementace (tělo) metody
Přístup k datům metody
Středník !!!
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.hmujprogram.cppImport
rozhraní
Automatický konstruktor
Instance třídy = objekt
zaludek = 1zaludek = 6zaludek = 3
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
2zaludek
pytlik:
beruska:
?
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);.....
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);.....
6zaludek 2zaludekpytlik: beruska:this
C C++
Každá metoda dostane 'tajný' parametr this – ukazatel na
objekt
zvire:: znamena
zvire * this
this->zije()
this->zaludek
Reference
int x = 1, y = 2;int *px;px = &x;*px = 3;
int &ry = y;ry = 4;
return *px + ry;
referencepouze
inicializacenelze měnit
3x: :px
4y: :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é parametryodkazy na proměnné
1x: :a
2y: :b
zpřehlednění kódupřetěžování funkcí
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čtemale různým typem parametrů
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)
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ápisycopy
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
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
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);
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
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
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+=b a[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))
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 konstruktordefinice 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
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; };
};
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
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í
konstruktorná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
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é chybyChtě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);
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
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á argumentynic nevrací
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 s1automaticky se vyvolá
destruktor
Ř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 popředchozím
řetězciprá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ě
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 !!!
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() ???
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ý operand
návratová hodnota
nelze návrat reference (lokální dočasný objekt)nové hodnoty VŽDY vracet
hodnotou
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
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
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
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;};
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
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žkypotomka
jez, vyměšujstav
žaludeksedni
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
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
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
azorZvire pytlik, *pz;Pes azor, *pp;
pytlik = azor;pz = &azor;
azor = pytlik;pp = &pytlik;
stav
žaludek
pytlik
žaludek
stav
žaludek žaludek
pytlikazor
???nelz
e
odlišné chování potomků – pozdní vazba (late binding)
Polymorfismus
najde něco v přírodě
Zvíře Pes Pitbul
Člověk
jez jez jez
jez
sní maso sní hodně masa
jde do restaurace
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řídzakrývání metod
Při překladu je známoze které třídy se volá
metoda
Každá třída má vlastní implementaci (tělo)
metody jez
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
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
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á
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á metodapodle tabulky virtuálních
funkcí
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
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
pozd
ní
vazb
a
bB::f
paaA::f
a
pab
pbb
kvalifi
kovan
é
volá
ní
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řídaspolečné rozhraní
POZOR!!!Nutný virtuální
destruktor
abstraktní třída nelze vytvořit
objektspolečný předek
Abstraktní třídy, virtuální destruktory
// Vojak v; // NELZE – abstraktní třídaPesak p; // OK – Pesak VojinPesak* pp = new Pesak; // OKpp->pal(); // Pesak::palVojak* pv = new Pesak; // OKpv->pal(); // Pesak::paldelete pp; // OK, Pesak::~Pesakdelete 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í
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
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 ::
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íratexplicitní přístup ke globálnímu identifikátoru ::idstandardní 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
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
#include <iostream>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ýstupjednotné rozhraní pro v/v do souborů a paměti, ...operátory << a >>, manipulátorymotivace:
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);
Streams – hierarchie tříd
<iostream> – základní operace, standardní v/v, manipulátory bez parametrů
cin, cout, <<, >>, endl, ws, ...
<iomanip> – manipulátory s parametry setw, setfill, ...
<fstream> – vstup a výstup do souborů fstream, ifstream, ofstream, ...
<strstream> - vstup a výstup do paměti (chytré řetězce) strstream, istrstream, ostrstream, ...
Hlavičkové soubory
do proudu lze vkládat manipulátory – změní stav proudu
endl pošle buffer na výstup a odřádkujeleft, right zarovnávej doleva / dopravadec, hex v desítkové / šestnáctkové soustavěws př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
#include <fstream>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řilasoubory není třeba zavírat,zavře je automaticky
destruktor
třída pro souborový proud
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 << a >>
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
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!
množina funkcí/tříd lišících se pouze typem parametrů/položekvzor, podle kterého překladač vytvoří funkci nebo třídu (instanci) pro konkrétní typ
template <typename T> 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ý
typmísto typename lze
class
int max( int a, int b) double max( double a, double b)
template<typename T> 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<int> 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
template<typename T> class Guma{ private: int size; T* array;public: T& operator[] (int n);};
template<typename T>T& Guma<T>::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<Krabice> polekrab;
int main(int argc, char* argv[]){ Guma<int> ip(5); polekrab pk; ip[3] = 999; pk[12].a = ip[3];
instancešablony
třídydefinice
typu
pk[i] je typu Krabice&
definice
šablony
metody
list<int> sez;sez.push_front( 1);sez.push_back( 2);sez.push_front( 3);list<int>::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 nimiiterá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ředuiterátor seznamu
průchod seznamem
přístup k datům přes iterátor – operator*
STL – kontejnery
Sekvenční kontejnery Asociativní kontejneryuspořádané setříděné
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
STL – iterátory a metody kontejnerů
kontejner<T>::iterator iterátor příslušného kontejneruT& iterator::operator* přístup k prvku přes iterátor
begin(), end() iterátor na začátek / za(!) konec kontejnerupush_front(), push_back() přidání prvku na začátek / konecpop_front(), pop_back() odebrání prvku ze začátku / konce – nevrací hodnotu!front(), back() prvek na začátku / koncioperator[], at() přímý přístup k prvkuinsert(iterator,T) vložení prvku na místo určené iterátoremsize(), empty(), clear() velikost / neprázdost / smazání kontejneru
push(), pop(), top() přidání / odebrání / prvek na vrcholu zásobníku
STL – použití iterátorů
vector<int> pole;vector<int>::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<int>
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
map<string,string> ts;
ts["Filip"] = "605123456";ts["Petra"] = "721334455";ts["David"] = "723654321";ts["Kuba"] = "222333444";
cout << "Telefon Petry: " << ts["Petra"] << endl;
map<string,string>::iterator ti;
for( ti = ts.begin(); ti != ts.end(); ti++) cout << ti->first << ": " << ti->second << endl;
STL – použití asociativního pole
pair<string,string> iterator::operator*
ti->first ≡ (*ti).first
ts:
pair:
string second
string first
operator [] (const string&)
vyhledání podle first
STL – algoritmy
Inicializacefill Fills a sequence with an initial valuefill_n Fills n positions with an initial valuecopy Copies a sequence into another sequencecopy_backward Copies a sequence into another sequencegenerate Initializes a sequence using a generatorgenerate_n Initializes n positions using a generator swap_ranges Swaps values from two parallel sequences
Vyhledávánífind Finds an element matching the argument
find_if Finds an element satisfying a conditionadjacent_find Finds consecutive duplicate elementsfind_first_of Finds one member of a seq. in another seq.
find_end Finds the last occurr. of a sub-seq. in a seq.
search Matches a sub-sequence within a sequence
max_element Finds the maximum value in a sequence
min_element Finds the minimum value in a sequence
mismatch Finds first mismatch in parallel sequences
Mazáníremove Removes elements that match conditionunique Removes all but first of duplicate values
Ostatnífor_each Applies a function to each element
Transformace prvkůreverse Reverses the elements in a sequencereplace Replaces specific values with new valuereplace_if Replaces elements matching predicaterotate Rotates elements in a sequence around a point
next_permutation Generates permutations in sequenceprev_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ísort Sorts all elementsmake_heap Converts a range into a heap
Skalární výpočtycount Counts number of elements matching value
count_if Counts elements matching predicateaccumulate Reduces sequence to a scalar valueequal Checks two sequences for equalitylexicographical_compare Compares two sequences
Výpočty generující sekvencetransform Transforms each elementpartial_sum Generates sequence of partial sumsadjacent_difference Gen. sequence of adjacent differences
+ mnoho dalších
STL – použití algoritmů
void tiskni( int x) { cout << " [" << x << "]"; }
vector<int> pole;vector<int>::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
STL – chybová hlášení\SRC\templ\templ.cpp(101) : error C2664: 'class std::_Tree<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,struct std::map<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >::_Kfn,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >::iterator __thiscall std::map<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >::insert(class std::_Tree<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,struct std::map<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >::_Kfn,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >::iterator,const struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > &)' : cannot convert parameter 1 from 'char [6]' to 'class std::_Tree<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,struct std::map<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >::_Kfn,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >::iterator'
string
Compare strings ==,!=,<,<=, >,>=,compare()
Concatenates strings +
Replaces characters replace()
Changes the number of characters (deletes or appends chars at the end)
resize()
Removes all characters (makes it empty)
clear()
Deletes characters erase()
Inserts characters insert()
Append characters +=,append(),push_back()
Swaps values between two strings swap()
Assign a new value =, assign()
Destroys a string destructor
Create or copy a string constructors
Returns the allocator get_allocator()
Provide reverse iterator support rbegin(), rend()
Provide normal iterator support begin(), end()
Search for a certain substring or character
find functions
Returns a certain substring substr()
Returns the value as character array data()
Returns the value as C-string c_str()
Copies or writes the contents to a C-string
copy()
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 empty empty()
Returns the maximum possible number of characters
max_size()
Return the number of characters size(), length()
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
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 exceptionpokusný
bloktry blocktry block
handler(y)typ výjimky
standardní metoda třídy runtime_error
řetězec z konstruktoru
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( 2000000000); 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
Výjimky - pravidla
k try bloku může být několik handlerů s různými typy try bloky mohou být vnořené výjimka může být vyvolána v libovolně zanořené funkci po vyvolání výjimky se řízení předá handleru s odpovídajícím typem před odchodem ze všech bloků se zavolají destruktory lokálních objektů
předávaná hodnota nese informaci o výjimce typické použití: potomek standardní třídy exception i pro výjimky platí, že potomek může nahradit předka konstruktor runtime_error(string&), metoda string what() po ošetření výjimky pokračuje program za handlery try bloku při běhu bez výjimky se handlery ignorují (přeskočí) neošetřená výjimka – unhandled exception, konec programu
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
... 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ě'
Dodelat na priste
Vice slajdu o pretezovani operatoru (i unarni)Vice slajdu o referenciChytre retezce – pocitane odkazy