Ú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
[email protected]://ulita.ms.mff.cuni.cz/~zavoral/VSFS
Studijní povinnostiPř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 IdiomsHerb 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
ZS (PJC) LS (OOP)
Obsah předmětu
C++
C
C++
C
Paradigmata programování, OOPObjekty, zapouzdření, dědičnostKonstruktory a destruktory, new a deletePozdní vazba, virtuální funkcePřetěžování funkcí, předefinování operátorůAbstraktní datové typyOO styly a idiomyVýjimky, šablony, knihovny, streams, STL, RTTI, ...
Obsah předmětu
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í
Paradigmata programování
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živateleImplementace – (neveřejná) interní data a metody
Třídy a objekty
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řídyzvire.
hvnitřní stav
(privátní)
rozhraní(veřejné)
datová položkainline tělo
funkce
konstruktor(inicializace
)metod
y
inline tělo funkce
deklarace metody
Zvíře - implementace
int zvire::jez( int jidlo){ if( ! zije()) return 0; return zaludek += jidlo;}
int zvire::vymesuj( int objem){ if( (zaludek -= objem) <= 0) zaludek = 0; return zaludek;}
zvire.cpp
třída metody
:: operátor kvalifikac
eimplementac
e (tělo) metody
přístup k datům třídy
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; .....}
mujprogram.cpp
instance třídy = objekt
automaticky
konstruktor
import rozhraní
zaludek = 1
zaludek = 3
-2 0
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
Metoda třídy - ke kterému objektu má
přistupovat?
zaludek
6
zaludek
2
pytlik:
beruska:
?
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);.....
Každá metoda dostane tajný
parametr this - ukazatel na objekt
zaludek
6
zaludek
2
pytlik:
beruska:
C-style řešení
this
Reference
int x = 1, y = 2;int *px;px = &x;*px = 3;
int &ry = y;ry = 4;
return *px + ry;
reference - pouze inicializacenelze měnit
3x: :px
4y: :ry
reference i ukazatele jsou reprezentovány
adresou
Reference jako parametry
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
význam:zpřehlednění kódupřetěžování
operátorů !
Přetěžování funkcí
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)
Funkce se stejným identifikátorem ale
různým počtem parametrů
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ů
Konstruktoryclass 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í) konstruktor
X(X&)
různé zápisy copy
konstruktoru
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;
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=( bod& b){ x = b.x; y = b.y; return *this;}
bod bod::operator+( 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
bod bod::operator+=( 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+=( bod& b){ return *this = *this + b; }
this->operator=( this->operator+( b))
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;
copy konstruktor a operator=
copy konstruktordeklarace nového objektu
operator=přiřazení do existujícího
obj.
není-li copy konstruktor nebo operator= definován, automaticky se vygeneruje
copy konstruktor resp. operator= všech složek
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
bod& rb = *new bod( 1,2);rb = rb + rb;delete &rb;
char* buf = new char[10];strcpy( buf, “ahoj”);...delete[] buf;
new a delete – pole a reference
alokace pole objektů
uvolnění paměti -
u alokovaných polí nutno []
dynamická inicializace reference
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á – chybyChtě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);
Chytré řetězce - třídaclass 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á
dataimplicitní
konstruktor – prázdný řetězec
destruktor – objekt si musí po sobě
uklidit !(delete přežije i 0)
operace s řetězci
další metody (délka řetězce)
Destruktoryclass str{private: char* buf;
public: str() { buf = 0; }; str( char* s) { buf = new char[ strlen( s) + 1]; strcpy( buf, s); };
~str() { delete[] buf; };};
ukazatel na alokovaná
dataalokace
paměti pro řetězecdestruktor –
automaticky se volá při zrušení
objektu
nemá argumenty, nic
nevracíobjekt po sobě musí uklidit
Vyvolání destruktoru
fce(){ str s1 = “ahoj”; str* s2 = new str( “babi”); ..... delete s2; .....}
v kostruktoru s1 se alokuje paměť
dynamická alokace sp2
delete zavolá destruktor
(a potom uvolní paměť)
zde končí život s1, automaticky
se vyvolá destruktor
Řetězce – implementacestr& 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){ ....}
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( 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; };};
O něco lepší implementace
privátní metoda – provádí vlastní alokaci a kopírování
častopotřebujeme uklízet
později si ukážeme ještě lepší –
counted pointers
void str::copy( 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ámeprázdný buf
zařídí volající metoda – copy je private
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){ ....}
Zřetězenínový prázdný řetězec
místo na znaky
první operand
druhý operand
návratová hodnota
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; };};
Připojení řetězce
když už umíme + a =proč si neudělat +=
operator+( str&) operator+( char*)
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);};
Spolupráce str a int (jednotlivé znaky)
dodefinovat konstruktor,
přiřazení a operace pro další typ
dodefinovat konstruktor,
přiřazení a operace pro další typ
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();
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( char* s);
public: str() { buf = 0; }; str( str& s); str( char* s); str( int c);
~str();
... and together
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();};
Vztah tříd předek-potomek – hierarchieVícenásobná dědičnost specializace (potomek má/umí něco navíc) reusabilita (jiné chování bez změny pův. třídy)
Dědičnost
protokoly - později
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; };
int sedni() { stav = Sedi; }; t_stav codela() { return stav; }};
potomek
zvířeteclass zvire{private: int zaludek;
public: zvire(); zvire( int jidlo);
int zije(); int jez( int jidlo); int vymesuj( int objem);};
stav
pes jako potomek – obsah objektu
položkypotomk
a
jez, vyměšuj
žaludek sedni
položky předka
metody předka
metody potomk
a
zvire pytlik;pes azor;
pytlik.jez();azor.jez();azor.sedni();
potomek obsahuje
všechny položky a metody předka
Konstruktor předka
class pes : public zvire{ ...
public: pes() { stav = Stoji; }; pes( int jidlo) : zvire( jidlo) { stav = Stoji; };};
implicitní konstruktor předka
(automaticky)
explicitní konstruktor předka
konstruktory předků a
vložených třídse volají před
konstruktorem potomka
Destruktor předkaclass 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 psazabijim zvire
Potomka lze přiřadit do předka (platí i pro ukazatele)Předka NELZE přiřadit do potomka (platí i pro uk.)
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žalude
k
pytlikžalude
k
stavžalude
kžalude
k
pytlik
azor
???nelze
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 restaurac
e
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
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()
nelze (syntaktická chyba, pes není
předkem zvířete)
zvire* z;
z = new pes;z->pes::jez(); // priroda();
z = new clovek;z->clovek::jez(); // priroda();
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
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 sebounese informaci
kterou virtuální funkci používá
Virtuální funkce - implementace
pes
stav
žaludek
zvirežalude
k
z = new zvire;
jez
zvire::jez() { priroda(); };
jez
pes::jez() { maso(1); };
z = new pes;
zvire * z;
z->jez();
tabulka virtuálních funkcí
tabulka virtuálních funkcí
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::AB b; // B::BA * paa = &a;A * pab = &b;B * pbb = &b;
a.f(); // A::fb.f(); // B::f
paa->f(); // A::f pab->f(); // B::fpbb->f(); // B::f
b.A::f(); // A::fb.B::f(); // B::fa.B::f(); // NE!paa->A::f(); // A::fpab->A::f(); // A::fpab->B::f(); // NE!pbb->A::f(); // A::fpbb->B::f(); // B::f
pozdní vazba
b B::f
paaA::fa
pab
pbb
kvalifikované volání
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 functionabstraktní třída
POZOR!!!Nutný virtuální
destruktor
Abstraktní třídy, virtuální destruktory
vojak v; // NELZE – abstraktní třídapesak p; // OK – pesak vojinpesak* pp = new pesak; // OKpp->pal(); // pesak::palvojak* v = new pesak; // OKv->pal(); // pesak::paldelete pp; // OK, pesak::~pesakdelete v; // !!! 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
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 prostorudefinice 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 jmenprostor 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
nová konvence: cstdio, cctype, iostream identifikátory uzavřené do namespace std
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
#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átory, snadné rozšiřování definice základních
tříd a manipulátorů
ostream cout FILE* stdout istream cin FILE* stdin
ostream& ostream::operator<< ( )
Streams – hierarchie tříd
<iostream> – základní operace, standardní v/v, manip. 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ásl. číselnou položku)setfill(int) výplňkový znak... a spousty dalších
[...17]cout << "[" << setfill('.') << setw(5) << 17 << "]" << endl;
Manipulátory
#include <fstream>using namespace std;
int main(){ fstream f( "C:\\src\\pokus.txt", ios::out); if( ! f) error(); f << "bubu" << endl;}
Výstup do souboruspojení proudu se
souborem v konstruktoru
způsob otevřeníios::in, out, app, trunc,
binary, ...
operator ! (ostream&)vrátí true když se
operace nepodařila
soubory není třeba zavírat, zavře je automaticky
destruktor
Další metody vstupních proudůpro binární vstup a výstup nelze použít operátory << a >>
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() peek()
Další metody výstupních proudů
Výstup get( kam, délka, koncový_znak) 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í/objektů lišících se pouze typem parametrů/položekvzor, podle kterého překladač vytvoří funkci nebo třídu - instanci
template <class 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ý
typ
int max( int a, int b)
double max( double a, double b)
template<class T> class guma{private: int size; T* array;public: enum { default_size = 10 }; guma( int _size = default_size) { size = _size; array = new T[size]; }; ~guma() { delete array; } T& operator[] (int n);};
int main(int argc, char* argv[]){ guma<int> ip(5); ip[3] = 999;
Šablony tříd - definice
přetížený operator[]
size: 5array:
0 1 2 3 4
? ? ? ? ?
instance šablony třídy
definice proměnné
pole neznáméh
o typu
template<class T> class guma{ private: int size; T* array;public: T& operator[] (int n);};
template<class 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 šablonstruct 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řídy
definice 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
deque – dvoustranná fronta (implementace: pomocí polí)stack, queue, priority_queue – fronta, zásobníkvector – polelist – dvousměrný seznam (implementace: spojový seznam)map, multimap – zobrazení, asociativní pole, slovník, mapa
(uspořádaná struktura indexovaná libovolným typem, pair: klíč, hodnota)set – množina (každý prvek nejvýše jednou)string – chytré řetězce (+, +=, mnoho dalších operací a metod)
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(T), push_back(T) přidání prvku na začátek / konecpop_front(), pop_back() odebrání prvku ze začátku / konce – nevrací!push(T), pop() přidání a odebrání z fronty / zásobníkufront(), back(), top() prvek na začátku fr. / konci fr. / vrcholu zás.operator[], 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
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*
ts: pair:
string second
string first
STL – generické algoritmyAlgorithms initializing a sequencefill 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
Searching algorithmsfind Finds an element matching the argument find_if Finds an element satisfying a condition adjacent_find Finds consecutive duplicate elements find_first_of Finds one member of a sequence in another seq.find_end Finds the last occurrence 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
Removal algorithms remove Removes elements that match conditionunique Removes all but first of duplicate values
Algorithms for in-place transformationsreverse Reverses the elements in a sequence replace Replaces specific values with new value replace_if Replaces elements matching predicaterotate Rotates elements in a sequence around a point partition Partitions elements into two groups stable_partition Partitions preserving original ordering next_permutation Generates permutations in sequence prev_permutation Generates permutations in reverse sequence
inplace_merge Merges two adjacent sequences into one random_shuffle Randomly rearranges elements in a sequence
Scalar generating algorithmscount Counts number of elements matching value count_if Counts elements matching predicateaccumulate Reduces sequence to a scalar value inner_product Gives inner product of two parallel sequencesequal Checks two sequences for equality lexicographical_compare Compares two sequences
Sequence generating algorithms transform Transforms each elementpartial_sum Generates sequence of partial sums adjacent_difference Generates sequence of adjacent differences
Miscellaneous operations for_each Applies a function to each element of a collection
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'
VýjimkyMotivace: 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 chybyVrátit 'divnou' hodnotu – takhle funguje většina knihovních funkcí Cnepří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říkladvyvolání výjimkytyp výjimky
standardní třídapotomek exception
pokusný blok
try blocktry blockhandler(y)typ výjimky
standardní metoda třídy runtime_error
řetězec z konstruktoru
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
const, protected, volatile, static, operátory .* a ->*, ukazatele na funkce, ...
vícenásobná dědičnost, protokoly RTTI, typeid, type_info static_cast, dynamic_cast, reinterpret_cast podrobněji knihovny
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í