385
Objektov Objektov ě-orientované ě-orientované p p rogramování v C++ rogramování v C++ David Bednárek ulita.ms.mff.cuni.cz

Objektov ě-orientované p rogramování v C++

  • Upload
    larue

  • View
    29

  • Download
    3

Embed Size (px)

DESCRIPTION

Objektov ě-orientované p rogramování v C++. David Bednárek ulita.ms.mff.cuni.cz. Pravidla studia. PRG0 32 2/2 Z,Zk. Zápis na cvičení. Elektronický zápis do jednotlivých skupin www.mff.cuni.cz/vnitro/is/sis Grupíček Skupiny budou naplněny podle příslušnosti ke studijním skupinám - PowerPoint PPT Presentation

Citation preview

Page 1: Objektov ě-orientované p rogramování v C++

ObjektovObjektově-orientované ě-orientované pprogramování v C++rogramování v C++

David Bednárek

ulita.ms.mff.cuni.cz

Page 2: Objektov ě-orientované p rogramování v C++

Pravidla studiaPravidla studia

PRG032

2/2

Z,Zk

Page 3: Objektov ě-orientované p rogramování v C++

Zápis na cvičení

Elektronický zápis do jednotlivých skupin

www.mff.cuni.cz/vnitro/is/sis• Grupíček

Skupiny budou naplněny podle příslušnosti ke studijním skupinám• Asi 30% studentů žije mimo studijní skupiny a nebyli takto zapsáni

Zapsáni musejí být všichni Do 13.10.

• Kdo se neobjeví na prvním ani druhém cvičení, bude vyškrtnut

Kapacita laboratoře je omezena, skupiny nelze přeplňovat

Udělit zápočet může jen cvičící,ke kterému je student zapsán

Kdo nebude do 13.10. zapsán, zápočet v tomto šk. roce nedostane

Page 4: Objektov ě-orientované p rogramování v C++

Udělení zápočtu

Přesné podmínky udělení zápočtu určuje cvičící Cvičící může podmínky individuálně upravit,

pokud se s ním student na začátku semestru dohodne

Vzorové podmínky Přiměřená účast na cvičeních Úspěšné odevzdání domácího úkolu Úspěšné složení zápočtového testu

• 1. a 2. pokusy ve zkouškovém období ... 3. pokusy v dubnu

• 2-3 hodiny v laboratoři, společně pro všechny skupiny

Vypracování zápočtového programu• Dohoda o tématu - do konce října

• Předvedení cvičícímu do 15.3.2005

• Doladění do konce výuky v letním semestru

Page 5: Objektov ě-orientované p rogramování v C++

Zkouška

Zkouška bude provedena formou abc-testu Vlastnosti a pravidla jazyka C++ Základy používání knihoven C++ (STL, iostream) Typické konstrukce objektového programování Exception-safe programming

Termíny Ve zkouškovém období ZS Během výuky v LS

Page 6: Objektov ě-orientované p rogramování v C++

Pravidla pro budoucí neúspěšné

Zkouška Pokud letos složíte obě zkoušky z PRG029 a PRG032 se známkou

výborně nebo velmi dobře a nedostanete některý zápočet, budou vám příští rok obě automaticky uznány

• Tento mechanismus je implementován zkoušejícími, nikoliv studijním oddělěním

• Příští rok se bude měnit rozdělení syllabu mezi semestry, proto toto pravidlo neplatí pro jednotlivé předměty

Zápočet Pokud nedostanete zápočet, budete příští rok opakovat ty části,

které jste letos nesplnili• Podmínky splněné letos se automaticky uznávají

• V příštím roce se musíte na začátku semestru přihlásit na některé cvičení a dohodnout se s jeho cvičícím na konkrétních podmínkách

• Mohou být vypsána speciální cvičení pro repetenty

Page 7: Objektov ě-orientované p rogramování v C++

ObsahObsah

Page 8: Objektov ě-orientované p rogramování v C++

Obsah cvičení

V laboratoři SW2 / v učebně

Microsoft Visual Studio .NET 2003

Demonstrace konstruktorů, destruktorů a virtuálních funkcí Příklady dědičnosti a virtuálních funkcí Demonstrace operátorů a copy-constructoru Příklady datových typů (Complex, String, Vector, Matrix) Praktická implementace vlastního typu string Šablony, implementace vlastních kontejnerů STL: deque, vector, map fstream, open etc. iostream, manipulátory, implementace vlastního operatoru << (Complex) Experimenty s výjimkami Exception-safe programování Debugging Vlastní nástroje pro ladění

Page 9: Objektov ě-orientované p rogramování v C++

Obsah přednášky

2.10. 3.10. Příklady dědičnosti, významy dědičnosti

Abstraktní a konkrétní třídy

Přetěžování operátorů, příklady: Complex a primitivní String

Lepší implementace třídy String, counted-pointers

Šablony

Šablony; Standardní knihovny C++

STL

Algoritmy, iostream

iostream, Koenig lookup

Výjimky

exception safety

Throw specifikace, přetypování, RTTI

Šablony – parciální specializace, traits, policies

Kanonické tvary tříd, Návrhové vzory, Singleton, Visitor

Page 10: Objektov ě-orientované p rogramování v C++

LiteraturaLiteratura

Page 11: Objektov ě-orientované p rogramování v C++

Literatura

Pro začátečníkyBruce Eckel:

Thinking in C++ (2000)Myslíme v jazyku C++ (Grada 2000)

Miroslav Virius:Pasti a propasti jazyka C++Programování v C++ (ČVUT 2001)

Andrew Koenig, Barbara E. Moo:Accelerated C++ (2000)

Stanley B. Lippman:Essential C++ (2000)

Page 12: Objektov ě-orientované p rogramování v C++

Literatura

Pro středně pokročiléAndrei Alexandrescu, Herb Sutter:

C++ Coding Standards (2005)Scott Meyers:

Effective C++ (1998)More Effective C++ (1996)Effective STL (2001)

Herb Sutter:Exceptional C++ (2000)More Exceptional C++ (2002)Exceptional C++ Style (2004)

Nicolai M. Josuttis:Object-Oriented Programming in C++ (2002)The C++ Standard Library (1999)

Page 13: Objektov ě-orientované p rogramování v C++

Literatura

Až si budete myslet, že všechno umíteAndrei Alexandrescu:

Modern C++ Design (2001)Moderní programování v C++ (Computer Press 2004)

David Vandevoorde, Nicolai M. Josuttis:C++ Templates (2003)

Page 14: Objektov ě-orientované p rogramování v C++

Normy

C++ISO/IEC JTC1/SC22/WG21:

14882 - Programming languages - C++ (2003)

C++ 2003 TR1 (2005) Nové rozšíření knihoven (částečně podporováno GCC 4.0) Založeno na knihovně Boost

C++0x Návrhy lze sledovat na http://www.open-std.org/jtc1/sc22/wg21/

C:ISO/IEC 9899:

Programming languages - C (1999) C99 Významně není podmnožinou C++ Řada překladačů jej nezvládá (včetně MSVC 2005)

Page 15: Objektov ě-orientované p rogramování v C++

www

http://www.open-std.org/jtc1/sc22/wg21/

ISO

http://www.gotw.ca/

Herb Sutter: Guru of the Week

http://www.boost.org/

Knihovna Boost

http://gcc.gnu.org/

GCC

Page 16: Objektov ě-orientované p rogramování v C++

C++C++

Page 17: Objektov ě-orientované p rogramování v C++

Třída a objekt

Třída (class)Zobecnění pojmu struktura (struct)

Rozdíl mezi class a struct v C++ je nepatrný Užívání class místo struct 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)

Objekt (instance třídy)Běhová reprezentace jednoho exempláře třídyReprezentace objektu v paměti obsahuje

Datové položky Skryté pomocné položky umožňující funkci

• virtuálních metod, výjimek a RTTI

• virtuální dědičnosti

Page 18: Objektov ě-orientované p rogramování v C++

Třída a objekt

Instanciace třídy = vznik objektu tohoto typuJako globální proměnná

V rámci startu programu (před main)

Jako lokální proměnná V okamžiku průchodu řízení deklarací

Jako parametr předávaný hodnotou Těsně před voláním funkce

Jako pomocná proměnná při výpočtu výrazu V okamžiku, kdy je vypočtena její hodnota

Dynamickou alokací V okamžiku volání operátoru new

Jako položka jiné třídy nebo součást pole V okamžiku vzniku celku

Jako předek jiné třídy V okamžiku vzniku instance potomka

Page 19: Objektov ě-orientované p rogramování v C++

Objekt a ukazatel na objekt

C++ důsledně odlišuje objekt a ukazatel na nějTriviální důsledek:

class T { /*...*/ };

T * p; // zde nevzniká objekt typu T

Page 20: Objektov ě-orientované p rogramování v C++

Objekt a ukazatel na objekt

C++ důsledně odlišuje objekt a ukazatel na nějTriviální důsledek:

class T { /*...*/ };

T * p; // zde nevzniká objekt typu T

Netriviální důsledek: Proměnná typu T je vždy objekt typu T a žádného jiného, přestože

do ní lze přiřadit i objekt odvozeného typuclass U : public T { /*...*/ };

U y;

T x = y; // toto je kopie části objektu y do vznikajícího objektu x

Page 21: Objektov ě-orientované p rogramování v C++

Objekt a ukazatel na objekt

C++ důsledně odlišuje objekt a ukazatel na nějTriviální důsledek:

class T { /*...*/ };

T * p; // zde nevzniká objekt typu T

Netriviální důsledek: Proměnná typu T je vždy objekt typu T a žádného jiného, přestože

do ní lze přiřadit i objekt odvozeného typuclass U : public T { /*...*/ };

U y;

T x = y; // toto je kopie části objektu y do vznikajícího objektu x

Poznámka pro znalce pravidel: K tomuto přiřazení může dojít díky existenci automaticky

vytvořeného copy-constructoruT::T( const T &);

a díky možnosti konvertovat odkaz na potomka na odkaz na předka:U => U & => T & => const T &

Page 22: Objektov ě-orientované p rogramování v C++

Objekt a ukazatel na objekt

C++ důsledně odlišuje objekt a ukazatel na nějTriviální důsledek:

class T { /*...*/ };

T * p; // zde nevzniká objekt typu T

Netriviální důsledek: Proměnná typu T je vždy objekt typu T a žádného jiného, přestože

do ní lze přiřadit i objekt odvozeného typuclass U : public T { /*...*/ };

U y;

T x = y; // toto je kopie části objektu y do vznikajícího objektu x

Poznámka pro znalce implementace: Zde (ani nikde jinde) se nekopírují odkazy na tabulky virtuálních

funkcí Proměnná typu T tedy zůstane typem T včetně přiřazení těl

virtuálních funkcí Jiné chování by nemělo smysl

Page 23: Objektov ě-orientované p rogramování v C++

Objekt a ukazatel na objekt

C++ důsledně odlišuje objekt a ukazatel na nějTriviální důsledek:

class T { /*...*/ };

T * p; // zde nevzniká objekt typu T

Netriviální důsledek: Proměnná typu T je vždy objekt typu T a žádného jiného, přestože

do ní lze přiřadit i objekt odvozeného typuclass U : public T { /*...*/ };

U y;

T x = y; // toto je kopie části objektu y do vznikajícího objektu x

V tomto odlišování se C++ liší od většiny jazyků s objekty (Java, JavaScript, PHP, VisualBasic, ...)

Page 24: Objektov ě-orientované p rogramování v C++

Objekt a ukazatel na objekt

C++ důsledně odlišuje objekt a ukazatel na nějNefunguje naivní implementace polymorfního typu:

class Variant { enum { REAL, COMPLEX } t; };

class Real : public Variant { public: double Re; };

class Complex : public Variant { public: double Re, Im; };

Variant max( Variant a, Variant b);

Real x, y, z = max( x, y); // nelze přeložit

Complex u, v, w = max( u, v); // nelze přeložit

Page 25: Objektov ě-orientované p rogramování v C++

Objekt a ukazatel na objekt

C++ důsledně odlišuje objekt a ukazatel na nějNefunguje naivní implementace polymorfního typu:

class Variant { enum { REAL, COMPLEX } t; };

class Real : public Variant { public: double Re; };

class Complex : public Variant { public: double Re, Im; };

Variant max( Variant a, Variant b);

Real x, y, z = max( x, y); // nelze přeložit

Complex u, v, w = max( u, v); // nelze přeložit

Parametry a, b nedokážou přenést atributy Re, Im Návratovou hodnotu nelze (ani explicitně) přetypovat na potomka

Real x, y, z = (Real)max( x, y); // nelze přeložit

Complex u, v, w = (Complex)max( u, v); // nelze přeložit

• I kdyby to šlo, typ Variant vracený hodnotou nedokáže přenést atributy Re, Im

Page 26: Objektov ě-orientované p rogramování v C++

Objekt a ukazatel na objekt

C++ důsledně odlišuje objekt a ukazatel na nějNefunguje naivní implementace polymorfního typu:

class Variant { enum { REAL, COMPLEX } t; };

class Real : public Variant { public: double Re; };

class Complex : public Variant { public: double Re, Im; };

V tomto případě lze tento problém řešit referencemi:

Variant & max( Variant & a, Variant & b);

• vyžaduje ovšem explicitní přetypování, které je nebezpečnéReal x, y, z = (Real &)max( x, y); // funguje

Complex u, v, w = (Complex &)max( u, v); // funguje

Vracení referencí ovšem funguje pouze pro funkce max a min

Page 27: Objektov ě-orientované p rogramování v C++

Objekt a ukazatel na objekt

C++ důsledně odlišuje objekt a ukazatel na nějNefunguje naivní implementace polymorfního typu:

class Variant { enum { REAL, COMPLEX } t; };

class Real : public Variant { public: double Re; };

class Complex : public Variant { public: double Re, Im; };

V tomto případě lze tento problém řešit referencemi:

Variant & max( Variant & a, Variant & b);

• vyžaduje ovšem explicitní přetypování, které je nebezpečnéReal x, y, z = (Real &)max( x, y); // funguje

Complex u, v, w = (Complex &)max( u, v); // funguje

Vracení referencí ovšem funguje pouze pro funkce max a min• Tyto funkce mají speciální vlastnost: vrací jeden ze svých parametrů

Page 28: Objektov ě-orientované p rogramování v C++

Objekt a ukazatel na objekt

Funkce jako add nemůže vracet referenciadd vrací hodnotu různou od všech svých parametrůhodnotu parametrů nesmí měnitreference nemá na co ukazovat

Špatné řešení č. 1: Lokální proměnná

Complex & add( const Complex & a, const Complex & b)

{

Complex r( a.Re + b.Re, a.Im + b.Im);

return r;

}

BĚHOVÁ CHYBA: r zaniká při návratu z funkce

Page 29: Objektov ě-orientované p rogramování v C++

Objekt a ukazatel na objekt

Funkce jako add nemůže vracet referenciadd vrací hodnotu různou od všech svých parametrůhodnotu parametrů nesmí měnitreference nemá na co ukazovat

Špatné řešení č. 2: Dynamická alokace

Complex & add( const Complex & a, const Complex & b)

{

Complex * r = new Complex( a.Re + b.Re, a.Im + b.Im);

return * r;

}

PROBLÉM: kdo to odalokuje ?

Page 30: Objektov ě-orientované p rogramování v C++

Objekt a ukazatel na objekt

Funkce jako add nemůže vracet referenciadd vrací hodnotu různou od všech svých parametrůhodnotu parametrů nesmí měnitreference nemá na co ukazovat

Špatné řešení č. 3: Globální proměnná

Complex g;

Complex & add( const Complex & a, const Complex & b)

{

g = Complex( a.Re + b.Re, a.Im + b.Im);

return g;

}

CHYBA: globální proměnná je sdílená

Complex a, b, c, d, e = add( add( a, b), add( c, d));

Page 31: Objektov ě-orientované p rogramování v C++

Třídy v C++

Konstrukce class, dědičnost a virtuální funkce jsou silný mechanismus, užívaný k různým účelům

Různé pohledy na třídy a různá pojmenování Abstraktní a konkrétní třídy Třídy jako datové typy Kontejnery (třídy logicky obsahující jiné objekty) Singletony (jednou instanciované třídy) Traits (neinstanciované třídy)

Různé účely dědičnosti Rozšíření požadovaného rozhraní Implementace požadovaného rozhraní Rozšíření implementované funkčnosti Využití k implementaci

Page 32: Objektov ě-orientované p rogramování v C++

Nesprávné užití dědičnosti

Nesprávné užití dědičnosti č. 1

class Real { public: double Re; };

class Complex : public Real { public: double Im; };

Vypadá jako reusabilita kódu

Page 33: Objektov ě-orientované p rogramování v C++

Nesprávné užití dědičnosti

Nesprávné užití dědičnosti č. 1

class Real { public: double Re; };

class Complex : public Real { public: double Im; };

Vypadá jako reusabilita kódu Porušuje pravidlo "každý potomek má všechny vlastnosti předka"

• např. pro vlastnost "má nulovou imaginární složku"

Page 34: Objektov ě-orientované p rogramování v C++

Nesprávné užití dědičnosti

Nesprávné užití dědičnosti č. 1

class Real { public: double Re; };

class Complex : public Real { public: double Im; };

Vypadá jako reusabilita kódu Porušuje pravidlo "každý potomek má všechny vlastnosti předka"

• např. pro vlastnost "má nulovou imaginární složku"

Důsledek - slicing:

double abs( const Real & p) { return p.Re > 0 ? p.Re : - p.Re; }

Complex x;

double a = abs( x); // tento kód LZE přeložit, a to je špatně

Důvod: Referenci na potomka lze přiřadit do reference na předka• Complex => Complex & => Real & => const Real &

Page 35: Objektov ě-orientované p rogramování v C++

Nesprávné užití dědičnosti

Nesprávné užití dědičnosti č. 1

class Real { public: double Re; };

class Complex : public Real { public: double Im; };

Slicing nastává i u předávání hodnotou

double abs( Real p) { return p.Re > 0 ? p.Re : - p.Re; }

Complex x;

double a = abs( x); // tento kód LZE přeložit, a to je špatně

Důvod: Předání hodnoty x do parametru p je provedeno implicitně vytvořeným konstruktorem:

Real::Real( const Real & y) { Re = y.Re; }

Parametr x typu Complex do tohoto konstruktoru lze předat• Complex => Complex & => Real & => const Real &

Page 36: Objektov ě-orientované p rogramování v C++

Nesprávné užití dědičnosti

Nesprávné užití dědičnosti č. 2

class Complex { public: double Re, Im; };

class Real : public Complex { public: Real( double r); };

Vypadá jako korektní specializace:"každé reálné číslo má všechny vlastnosti komplexního čísla"

Page 37: Objektov ě-orientované p rogramování v C++

Nesprávné užití dědičnosti

Nesprávné užití dědičnosti č. 2

class Complex { public: double Re, Im; };

class Real : public Complex { public: Real( double r); };

Vypadá jako korektní specializace:"každé reálné číslo má všechny vlastnosti komplexního čísla"

Chyba: Objekty v C++ nejsou hodnoty v matematice Třída Complex má vlastnost "lze do mne přiřadit Complex"

• Tuto vlastnost třída Real logicky nemá mít, s touto dědičností ji mít bude

Page 38: Objektov ě-orientované p rogramování v C++

Nesprávné užití dědičnosti

Nesprávné užití dědičnosti č. 2

class Complex { public: double Re, Im; };

class Real : public Complex { public: Real( double r); };

Vypadá jako korektní specializace:"každé reálné číslo má všechny vlastnosti komplexního čísla"

Chyba: Objekty v C++ nejsou hodnoty v matematice Třída Complex má vlastnost "lze do mne přiřadit Complex"

• Tuto vlastnost třída Real logicky nemá mít, s touto dědičností ji mít bude

void set_to_i( Complex & p) { p.Re = 0; p.Im = 1; }

Real x;

set_to_i( x); // tento kód LZE přeložit, a to je špatně

Důvod: Referenci na potomka lze přiřadit do reference na předka• Real => Real & => Complex &

Page 39: Objektov ě-orientované p rogramování v C++

Nesprávné užití dědičnosti

Nesprávné užití dědičnosti č. 2

class Complex { public: double Re, Im; };

class Real : public Complex { public: Real( double r); };

Vypadá jako korektní specializace:"každé reálné číslo má všechny vlastnosti komplexního čísla"

Chyba: Objekty v C++ nejsou hodnoty v matematice Třída Complex má vlastnost "lze do mne přiřadit Complex"

• Tuto vlastnost třída Real logicky nemá mít, s touto dědičností ji mít bude

Poznámka: při přímem přiřazování tento problém nenastane

Complex y;

Real x;

x = y; // tento kód NELZE přeložit

• Důvod: operátor = se nedědíComplex & Complex::operator=( const Complex &); // nezdědí se

Real & Real::operator=( const Real &); // nesouhlasí typ argumentu

Page 40: Objektov ě-orientované p rogramování v C++

Třídy v C++

Třídy sloužící jako datové typyProměnné typu TČasté kopírování, vracení hodnotou

Přiřazení bývá jediný způsob změny stavu objektu

Dědičnost nemá smysl Bez virtuálních funkcí

Třídy reprezentující „živé“ objektyProměnné typu T *, případně T &

Objekty alokovány dynamicky

Kopírování nemívá smyslMetody měnící stav objektuVětšinou s dědičností a virtuálními funkcemi

Page 41: Objektov ě-orientované p rogramování v C++

Třídy v C++

Abstraktní třídaDefinice v C++: Třída obsahující alespoň jednu čistě

virtuální funkciBěžná definice: Třída, která sama nebude instanciovánaPředstavuje rozhraní, které mají z ní odvozené třídy

(potomci) implementovat

Konkrétní třídaTřída, určená k samostatné instanciaciImplementuje rozhraní, předepsané abstraktní třídou, ze

které je odvozena

Page 42: Objektov ě-orientované p rogramování v C++

Dědičnost a destruktor

class UUU {

public:

virtual ~UUU();

};

class XXX : public UUU {

public:

virtual ~XXX();

};

XXX * px = new XXX;

// konverze potomek-předek

UUU * pu = px;

delete pu;

Pokud je objekt destruován operátorem delete aplikovaným na ukazatel na předka, musí být destruktor v tomto předku deklarován jako virtuální

Odvozené pravidlo: Každá abstraktní třída má mít

virtuální destruktor Je to zadarmo Může se to hodit

Page 43: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Abstraktní třídaDefinuje rozhraní objektu jako množinu předepsaných

virtuálních funkcí

class GraphicObject {

public:

virtual ~GraphicObject(); // každá abstraktní třída má mít v.d.

virtual void paint() = 0; // čistě virtuální funkce

virtual void move( int dx, int dy) = 0; // čistě virtuální funkce

};

Page 44: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Abstraktní třídaDefinuje rozhraní objektu jako množinu předepsaných

virtuálních funkcíAbstraktní třídy se mohou dědit

Dědičnost jako rozšiřování předepsaného rozhraní

class ClickableObject : public GraphicObject {

public:

virtual void click( int x, int y) = 0; // čistě virtuální funkce

};

Page 45: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Abstraktní třídaDefinuje rozhraní objektu jako množinu předepsaných

virtuálních funkcíKonkrétní třída

Implementuje předepsané virtuální funkceJe potomkem abstraktní třídy

Dědičnost jako vztah rozhraní-implementace

Page 46: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Abstraktní třídaDefinuje rozhraní objektu jako množinu předepsaných

virtuálních funkcíKonkrétní třída

Implementuje předepsané virtuální funkce

class Button : public ClickableObject {

public:

Button( int x, int y, const char * text);

protected:

virtual void paint();

virtual void move( int dx, int dy);

virtual void click( int x, int y);

private:

int x_, y_; char * text_;

};

Page 47: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Abstraktní třídaDefinuje rozhraní objektu jako množinu předepsaných

virtuálních funkcíKonkrétní třída

Implementuje předepsané virtuální funkcePolotovar třídy

Mezi abstraktní a konkrétní třídou - třída implementující část předepsaných virtuálních funkcí

Jejím potomkem je konkrétní třída nebo jiný polotovar Dědičnost jako reusabilita kódu

Page 48: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Polotovar třídyMezi abstraktní a konkrétní třídou - třída implementující

část předepsaných virtuálních funkcí

class PositionedObject : public ClickableObject {

public:

PositionedObject( int x, int y);

protected:

int get_x() const { return x_; }

int get_y() const { return y_; }

virtual void move( int dx, int dy);

private:

int x_, y_;

};

Page 49: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Konkrétní třídaImplementuje předepsané virtuální funkceKonkrétní třídy mohou mít potomky - jiné konkrétní třídy se

změněnými vlastnostmi (redefinovanými virtuálními funkcemi)

Dědičnost jako reusabilita kódu se změnou chování

class IconButton : public Button {

public:

IconButton( int x, int y, const char * text, BitMap icon);

protected:

virtual void paint();

private:

BitMap icon_;

};

Page 50: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Různé významy dědičnosti Rozšiřování předepsaného rozhraní

• GraphicObject => ClickableObject

Vztah rozhraní-implementace• ClickableObject => PositionedObject, Button

Reusabilita kódu• PositionedObject => Button

Reusabilita se změnou chování (overriding)• Button => IconButton

A to není zdaleka všechno...

C++ pro odlišné účely využívá tytéž mechanismyNěkteré jazyky tyto účely rozlišují (Java)

Page 51: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Ideální abstraktní třídaPouze čistě virtuální funkceŽádná data, žádná těla funkcíNěkdy (nesprávně) nazývána protokol

Pojem Protokol většinou znamená seznam funkcí a pravidla pro pořadí jejich volání, což C++ nedovede vyjádřit

Page 52: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Ideální abstraktní třídaPouze čistě virtuální funkceŽádná data, žádná těla funkcíNěkdy (nesprávně) nazývána protokol

Pojem Protokol většinou znamená množinu funkcí a pravidla pro pořadí jejich volání, což C++ nedovede vyjádřit

Takové množiny funkcí má smysl kombinovat Významem je sjednocení množin schopností Příklad: Fyzikář+Matikář

Page 53: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Ideální abstraktní třídaPouze čistě virtuální funkceŽádná data, žádná těla funkcíNěkdy (nesprávně) nazývána protokol

Pojem Protokol většinou znamená množinu funkcí a pravidla pro pořadí jejich volání, což C++ nedovede vyjádřit

Takové množiny funkcí má smysl kombinovat Významem je sjednocení množin schopností Příklad: Fyzikář+Matikář

V C++: násobná dědičnostObvykle musí být virtuální, aby odpovídala sjednocení:

Fyzikář = Pedagogika + Fyzika Matikář = Pedagogika + Matematika Fyzikář+Matikář nemá mít dvě rozhraní pro Pedagogiku

Page 54: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Podmínka užitečnosti virtuálních funkcíFunkce musí být volána na objektu, jehož skutečný typ není

v době kompilace znám Nesmí to být proměnná typu třída Musí to být ukazatel nebo reference na třídu

Page 55: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Typické použití virtuálních funkcíPolymorfní datová struktura

Datová struktura (pole, seznam, strom, ...) obsahující objekty různých typů

Tyto objekty musí být vázány odkazem• Typicky bývají samostatně dynamicky alokovány

Na objektech se obvykle vykonávají hromadné abstraktní operace (např. vykreslení)

• Volání čistě virtuální funkce (paint) na každém objektu

Page 56: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Typické použití virtuálních funkcíPolymorfní datová struktura

class Scene {

public:

void insert( GraphicObject * go);

void paintAll() const;

private:

enum { MAX_ = 30 };

GraphicObject * objects_[ MAX_];

int n_;

};

primitivní řešení: pole ukazatelů• pole objektů by nefungovalo (ani nešlo přeložit)

void Scene::paintAll()

{ for ( int i = 0; i < n_; i++) objects_[ i]->paint();

}

Page 57: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Typické použití virtuálních funkcíPolymorfní datová struktura

struct Item { GraphicObject * go; Item * next; };

class Scene {

public:

void insert( GraphicObject * go);

void paintAll() const;

private:

Item * first_;

};

lepší řešení: spojový seznam ukazatelů• Item nemůže přímo obsahovat GraphicObject

void Scene::paintAll()

{ for ( Item * p = first_; p; p = p->next)

p->go->paint();

}

Page 58: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Typické použití virtuálních funkcíPolymorfní datová struktura

jiné lepší řešení: intrusivní spojový seznam • samotný GraphicObject slouží jako prvek spojového seznamu

• nevýhoda: abstraktní rozhraní zároveň obsahuje implementaciclass GraphicObject {

/* ... čistě virtuální funkce ... */

private:

friend class Scene;

Item * next_;

};

class Scene {

public:

void insert( GraphicObject * go);

void paintAll() const;

private:

GraphicObject * first_;

};

Page 59: Objektov ě-orientované p rogramování v C++

Ideální užití dědičnosti a virtuálních funkcí

Typické použití virtuálních funkcíPolymorfní datová struktura

moderní řešení: STL • kontejnery z STL vždy obsahují kopie vkládaných objektů

• std::list< GraphicObject> by nešlo ani přeložit

• kontejner je tedy třeba aplikovat na ukazatel, podobně jako pole

class Scene {

public:

void insert( GraphicObject * go);

void paintAll() const;

private:

std::list< GraphicObject *> objects_;

};

Page 60: Objektov ě-orientované p rogramování v C++

Třída jako datový typTřída jako datový typ

Page 61: Objektov ě-orientované p rogramování v C++

Speciální metody tříd

Konstruktor bez parametrů (default constructor)XXX();

Používán u proměnných bez inicializace Kompilátor se jej pokusí vygenerovat, je-li to třeba a pokud třída

nemá vůbec žádný konstruktor:• Položky, které nejsou třídami, nejsou generovaným konstruktorem

inicializovány

• Generovaný konstruktor volá konstruktor bez parametrů na všechny předky a položky

• To nemusí jít např. pro neexistenci takového konstruktoru

Kopírovací konstruktor (copy constructor)XXX( const XXX &);

Používán pro předávání parametrů a návratových hodnot Kompilátor se jej pokusí vygenerovat, je-li to třeba a pokud třída

kopírovací konstruktor nemá:• Položky, které nejsou třídami, jsou kopírovány

• Na předky a položky se volá kopírovací konstruktor

• To nemusí jít kvůli ochraně přístupu

Page 62: Objektov ě-orientované p rogramování v C++

Speciální metody tříd

Operátor přiřazení (assignment operator)const XXX & operator=( const XXX &);

Implementace operátoru = pro typ XXX na levé straně Kompilátor se jej pokusí vygenerovat, je-li to třeba a třída jej nemá:

• Položky, které nejsou třídami, jsou kopírovány

• Na předky a položky se volá operátor přiřazení

• To nemusí jít kvůli ochraně přístupu

Destruktor~XXX();

Používán při zániku objektu Kompilátor se jej pokusí vygenerovat, je-li to třeba a třída jej nemá

• To nemusí jít kvůli ochraně přístupu

Pokud je objekt destruován operátorem delete aplikovaným na ukazatel na předka, musí být destruktor v tomto předku deklarován jako virtuální

virtual ~XXX();

Page 63: Objektov ě-orientované p rogramování v C++

Speciální metody tříd

Konverzní konstruktoryclass XXX {

XXX( YYY);

};

Zobecnění kopírovacího konstruktoru Definuje uživatelskou konverzi typu YYY na XXX Je-li tento speciální efekt nežádoucí, lze jej zrušit:

explicit XXX( YYY);

Konverzní operátoryclass XXX {

operator YYY() const;

};

Definuje uživatelskou konverzi typu XXX na YYY Vrací typ YYY hodnotou (tedy s použitím kopírovacího konstruktoru

YYY, pokud je YYY třída)

Kompilátor vždy použije nejvýše jednu uživatelskou konverzi

Page 64: Objektov ě-orientované p rogramování v C++

Přetěžování operátorůPřetěžování operátorů

Operator overloading

Page 65: Objektov ě-orientované p rogramování v C++

Přetěžování operátorů

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 tudíž 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, např.: ++a nemusí být ekvivalentní a=a+1 a[b] nemusí být ekvivalentní *(a+b) ani b[a]

Pro předefinované operátory && a || neplatí pravidla o zkráceném vyhodnocování

Page 66: Objektov ě-orientované p rogramování v C++

Přetěžování operátorů

Typy skutečných operandů předefinovaného operátoru nemusejí přesně odpovídat typům formálních parametrů operátoru.

Pro výběr správné varianty mezi předefinovanými operátory platí stejná pravidla, jako pro přetížené funkce.

Předefinování operátorů se provádí definováním metody se speciálním jménem operatorxxx ve třídě (prvního operandu), pro kterou má být operátor definován.

Některé operátory je možno definovat i jako globální funkce s týmž speciálním jménem.

Speciální jméno je možno používat i pro explicitní vyvolání této metody či funkce.

Operátory, které jsou metodami, jsou s výjimkou operátoru přiřazení dědičné a smějí být virtuální.

Page 67: Objektov ě-orientované p rogramování v C++

Přetěžování operátorů - Binární operátory

Binární operátor xxx z množiny+ - * / % << >> < > <= >= <<= >>= ^ & | && || == != += -= *= /= %= ^= &= |= ->*

lze pro operandy typu B a C předefinovat dvěma způsoby: Globální funkcí

A operator xxx( B, C)

A operator xxx( B &, C &)

A operator xxx( const B &, const C &)

MetodouA B::operator xxx( C)

A B::operator xxx( const C &)

A B::operator xxx( const C &) const

Binární operátor [ ] lze předefinovat pouze metodou

A B::operator []( C)

A B::operator []( C &)

A B::operator []( const C &) const

Page 68: Objektov ě-orientované p rogramování v C++

Přetěžování operátorů - Unární operátory

Unární operátor xxx z množiny+ - * & ~ !

a prefixové operátory++ --

lze pro operand typu B předefinovat dvěma způsoby: Globální funkcí

A operator xxx( B)

A operator xxx( B &)

A operator xxx( const B &)

MetodouA B::operator xxx()

A B::operator xxx() const

Page 69: Objektov ě-orientované p rogramování v C++

Přetěžování operátorů - Unární operátory

Postfixové operátory ++ a --lze pro operand typu B předefinovat dvěma způsoby:

Globální funkcíA operator xxx( B, int)

A operator xxx( B &, int)

A operator xxx( const B &, int)

MetodouA B::operator xxx( int)

A B::operator xxx( int) const

Page 70: Objektov ě-orientované p rogramování v C++

Přetěžování operátorů - Unární operátory

Operátor ->je považován za unární operátor a jeho návratovou

hodnotou musí být buďto ukazatel na třídu s uvedenou položkou, nebo objekt či referenci na objekt, pro který je znovu definován operátor ->

Page 71: Objektov ě-orientované p rogramování v C++

Přetěžování operátorů - Unární operátory

Operátor volání funkce ()smí být definován pouze jako metoda třídy a umožňuje

používat objekty této třídy jako funkce. Smí mít libovolný počet parametrů a pro výběr konkrétní

varianty operátoru se použije podobný mechanismus, jako pro přetížené funkce.

Page 72: Objektov ě-orientované p rogramování v C++

ComplexComplex

Komplexní číslo

Page 73: Objektov ě-orientované p rogramování v C++

Complex

class Complex {

public:

Complex() : re_( 0.0), im_( 0.0) {}

Complex( double re, double im = 0.0) : re_( re), im_( im) {}

double Re() const { return re_; }

double Im() const { return im_; }

Complex operator+( const Complex & b) const

{

return Complex( Re() + b.Re(), Im() + b.Im());

}

Complex operator+( double b) const

{

return Complex( Re() + b, Im());

}

private:

double re_, im_;

};

inline Complex operator+( double a, const Complex & b)

{

return Complex( a + b.Re(), b.Im());

Page 74: Objektov ě-orientované p rogramování v C++

Poučení - konstruktory

Ve třídě Complex nejsou odkazy na data uložená jindeVyhovuje chování těchto kompilátorem vytvořených metod:

Complex( const Complex &); Kopíruje datové položky

Complex & operator=( const Complex &); Kopíruje datové položky

~Complex(); Nedělá nic

Tyto metody není třeba psát vlastní

Page 75: Objektov ě-orientované p rogramování v C++

Poučení - konstruktory

Ve třídě Complex jsou datové položky atomických typů Ty nemají konstruktory a zůstávají neinicializované

Nevyhovalo by tedy chování kompilátorem vytvořeného konstruktoru bez parametrů:

Complex(); Nedělá nic

Navíc je zde jiný konstruktor, takže kompilátor má zakázáno konstruktor bez parametrů vytvořit

Nebylo by tedy možné deklarovat proměnnou typu Complex bez explicitní inicializace

Konstruktor bez parametrů musí být napsán ručně Měl by nastavit datové položky na vhodnou hodnotu

Page 76: Objektov ě-orientované p rogramování v C++

Poučení - konstruktory

Speciální konstruktorComplex( double re, double im = 0.0);

Lze zavolat s jedním parametrem

Slouží jako konverzní konstruktor Implementuje konverzi double => Complex

Důsledky: Bude fungovat přiřazení Complex=double Není nutné psát sčítání pro Complex+double:

Complex Complex::operator+( double b) const;

• Dělá se to kvůli rychlosti

Kompilátor umí použít konverzi (jednu) a najít náhradní metodu:Complex Complex::operator+( const Complex & b) const;

Nefunguje pro sčítání double+Complex• Protože Complex+Complex je implementováno metodou

• Při implementaci globální funkcí by to fungovaloComplex operator+( const Complex & a, const Complex & b)

Page 77: Objektov ě-orientované p rogramování v C++

Poučení – konverzní operátor

Konverzní operátoroperator double() const;

Implementuje konverzi Complex => double

Není zde vhodný Konverze je ztrátová Může vést k nejednoznačnostem

Complex a, b; double c;

a = b + c;

• může znamenat voláníComplex::Complex( double re, double im = 0.0); Complex operator+( const Complex & a, const Complex & b);

Complex::operator double() const;double double::operator=( double b); // vestavěná operace

• ale takéComplex::operator double() const;double operator+( double a, double b); // vestavěná operacedouble double::operator=( double b); // vestavěná operace

• Kompilátor si (z definice) neumí vybrat – hlásí chybu

• Výběr funkce/operátoru je řízen pouze typy argumentů

Page 78: Objektov ě-orientované p rogramování v C++

Poučení – konverzní operátor

Konverzní operátoroperator double() const;

Implementuje konverzi Complex => double

Není zde vhodný Konverze je ztrátová Může vést k nejednoznačnostem Může vést k chybám

Complex a, b;

a = sin( b);

• může znamenat voláníComplex sin( const Complex & x); // <complexmath.h>

• ale takéComplex::operator double() const;

double sin( double x); // <math.h>

Complex( double re, double im = 0.0);

• První varianta má přednost...

• ...ale když zapomenete #include <complexmath.h> ...

Page 79: Objektov ě-orientované p rogramování v C++

Poučení - vracení hodnotou

Once programmers grasp the efficiency implications of pass-by-value for objects, they become crusaders, determined to root out the evil of pass-by-value wherever it may hide. [Scott Meyers: Effective C++]

Page 80: Objektov ě-orientované p rogramování v C++

Poučení - vracení hodnotou

Once programmers grasp the efficiency implications of pass-by-value for objects, they become crusaders, determined to root out the evil of pass-by-value wherever it may hide. [Scott Meyers: Effective C++]

Poučení: operator+ vždy vrací hodnotouVrací novou hodnotu, která jinde neexistuje

return Complex( ...);

Page 81: Objektov ě-orientované p rogramování v C++

Poučení - vracení hodnotou

Once programmers grasp the efficiency implications of pass-by-value for objects, they become crusaders, determined to root out the evil of pass-by-value wherever it may hide. [Scott Meyers: Effective C++]

Poučení: operator+ vždy vrací hodnotouVrací novou hodnotu, která jinde neexistuje

return Complex( ...);

Ale: operator+= může vracet odkazemVrací hodnotu levého operandu

return * this;

Page 82: Objektov ě-orientované p rogramování v C++

Poučení – binární operátory +, +=

Kanonické řešeníclass Complex {

public:

Complex( double re, double im = 0.0); // konverzní konstruktor

// ...

Complex & operator+=( const Complex & b)

{

re_ += b.re_;

im_ += b.im_;

return * this;

}

// ...

};

Complex operator+( const Complex & a, const Complex & b)

{

Complex tmp( a);

tmp += b;

return tmp;

}

Page 83: Objektov ě-orientované p rogramování v C++

Poučení – unární operátory -, ++

Kanonické řešení Unární operátory jsou vždy metodami

• Není zapotřebí schopnost konverze operandůclass Complex {

public:

// ...

Complex operator-() const { return Complex( -re_, -im_); }

Complex & operator++() { _re += 1.0; return * this; }

Complex operator++( int)

{ Complex tmp( * this);

operator++();

return tmp;

}

};

Prefixové ++, -- vrací odkazem• Může a nemusí být const

Postfixové ++, -- vrací hodnotou

Page 84: Objektov ě-orientované p rogramování v C++

Poučení – unární operátory -, ++

Kanonické řešení Unární operátory jsou vždy metodami

• Není zapotřebí schopnost konverze operandůclass Complex {

public:

// ...

Complex operator-() const { return Complex( -re_, -im_); }

Complex & operator++() { _re += 1.0; return * this; }

Complex operator++( int)

{ Complex tmp( * this);

operator++();

return tmp;

}

};

Poučení pro uživatele operátorů ++, --Prefixová varianta je významně rychlejší

S výjimkou vestavěných typů

Page 85: Objektov ě-orientované p rogramování v C++

StringString

Ukázková implementace č. 1(nepoužitelná)

Page 86: Objektov ě-orientované p rogramování v C++

String č. 1 - Nepoužitelné řešení

class String {

public:

String() { _str[ 0] = 0; }

 

String( const char * s) { strcpy( _str, s); }

const char * c_str() const { return _str; }

 

friend String operator+( const String & a, const String & b);

 

private:

enum { MAX = 256 };

char _str[ MAX];

};

 

String operator+( const String & a, const String & b)

{ String c;

strcpy( c._str, a._str); strcat( c._str, b._str);

return c;

}

Page 87: Objektov ě-orientované p rogramování v C++

Poučení - Konverzní operátor

Metoda c_str by mohla být nahrazena konverzním operátorem

operator const char *() const;

V tomto případě to není vhodné:

String x;

if ( x > "A" )

Může být převedeno na String > String(pokud je definován takový operátor)ale také na const char * > const char *

• Porovnává adresy !

printf( "%s", x);

Zde k žádné konverzi nedojde !• Program bude pravděpodobně ukončen pro pokus o nedovolenou

operaci

Page 88: Objektov ě-orientované p rogramování v C++

StringString

Ukázková implementace č. 2

(naivní, neefektivní)

Page 89: Objektov ě-orientované p rogramování v C++

String č. 2 - Naivní řešení

class String {public: String(); String( const String &); const String & operator=( const String &); ~String();  String( const char *); const char * c_str() const;  String cat( const String &) const; private: char * _str;};

inline String operator+( const String & a, const String & b)

{

return a.cat( b);

}

Page 90: Objektov ě-orientované p rogramování v C++

Poučení - konstruktory

Ve třídě String jsou odkazy na data uložená jindeChování kompilátorem vytvořených metod nevyhovuje:

String(); Nedělá nic - je třeba naalokovat prázdný string

String( const String &); Kopíruje ukazatel - je třeba kopírovat data, na která ukazuje

String & operator=( const String &); Totéž, navíc je nutné před přiřazením uklidit

~String(); Nedělá nic - je třeba uklidit

Tyto metody je tedy třeba napsat vlastní

Page 91: Objektov ě-orientované p rogramování v C++

Poučení - konstruktory

Operátor přiřazení by měl udělat toto: Zrušit starý obsah levé strany

• Totéž co destruktor

Okopírovat pravou stranu• Totéž co konstruktor

Vrátit novou hodnotu levé strany• To lze vrátit odkazem

Pozor - v některých případech to fungovat nebude:

String a = "ha";

a = a;

Při kopírovaní pravá strana už nebude existovat

Page 92: Objektov ě-orientované p rogramování v C++

Poučení - konstruktory

Vzorový operátor přiřazení:

String & String::operator=( const String & b)

{

if ( this != & b )

{ clean();

fill( b);

}

return * this;

}

Konstruktory nelze přímo volat, u destruktoru to není rozumné

String::String( const String & b)

{

fill( b);

}

String::~String()

{

clean();

}

Page 93: Objektov ě-orientované p rogramování v C++

Poučení - konstruktory

Lepší řešení operátoru přiřazení:#include <algorithm>

void String::swap( String & b)

{

std::swap< _str, b._str>;

}

String & String::operator=( const String & b)

{

String tmp( b);

swap( tmp);

return * this;

}

Toto řešení je navíc exception-safe (později...) Metodu swap je vhodné publikovat takto

void swap( String & a, String & b)

{ a.swap( b); }

• Kdyby někdo udělal třídu obsahující náš String...

Page 94: Objektov ě-orientované p rogramování v C++

StringString

Ukázková implementace č. 3(counted-pointers)

Page 95: Objektov ě-orientované p rogramování v C++

String č. 3 - Counted-pointers

class StringBody;

class String {

public:

String();

String( const String &);

const String & operator=( const String &);

~String();

 

String( const char *);

operator const char *() const;

 

String cat( const String &) const;

 

private:

String( StringBody *);

 

StringBody * _body;

};

Page 96: Objektov ě-orientované p rogramování v C++

String č. 3 - Counted-pointers

class StringBody {

friend class String;

private:

StringBody();

StringBody( int);

~StringBody();

 

void inc() { _count++; };

void dec();

char * buffer() { return _str; };

 

char * _str;

int _count;

 

static StringBody empty;

};

Page 97: Objektov ě-orientované p rogramování v C++

String č. 3 - Counted-pointers

Kopie a destrukceString::String( const String & b)

{

(_body = b._body)->inc();

}

 

const String & String::operator=( const String & b)

{

if ( _body != b._body )

{

_body->dec();

(_body = b._body)->inc();

}

return * this;

}

 

String::~String()

{

_body->dec();

}

Page 98: Objektov ě-orientované p rogramování v C++

String č. 3 - Counted-pointers

Destrukce těla

void StringBody::dec()

{

if ( ! --count )

delete this;

};

StringBody::~StringBody()

{

delete[] _str;

}

 

Page 99: Objektov ě-orientované p rogramování v C++

String č. 3 - Counted-pointers

Vytvoření neprázdného těla

StringBody::StringBody( int l)

{

_count = 0;

_str = new char[ l];

}

 

String::String( StringBody * b)

{

(_body = b)->inc();

}

String::String( const char * s)

{

_body = new StringBody( strlen( s) + 1);

_body->inc();

strcpy( _body->_str, s);

}

Page 100: Objektov ě-orientované p rogramování v C++

String č. 3 - Counted-pointers

Speciální implementace prázdného řetězce

StringBody::StringBody()

{

_str = "";

_count = 1;

}

 

StringBody StringBody::empty;

String::String()

{

(_body = &StringBody::empty)->inc();

}

 

Page 101: Objektov ě-orientované p rogramování v C++

StringString

operator [ ]

Page 102: Objektov ě-orientované p rogramování v C++

String č. 4 - operator [ ] - čtení

class String {

public:

 

char operator[]( int pos) const

{

if ( pos < 0 || pos >= _body->length() )

return 0;

return _body->buffer()[ _pos];

};

/* ... */

};

Page 103: Objektov ě-orientované p rogramování v C++

String č. 4 - operator [ ] - zápis

class String {

public:

 

char & operator[]( int pos)

{

if ( pos < 0 || pos >= _body->length() )

return _dummy;

return _body->buffer()[ _pos];

};

private:

static char _dummy;

/* ... */

};

char String::_dummy = 0;

Page 104: Objektov ě-orientované p rogramování v C++

String č. 4 - operator [ ] - zápis

class String {

public:

 

char & operator[]( int pos)

{

if ( pos < 0 || pos >= _body->length() )

return _dummy;

return _body->buffer()[ _pos]; // chyba: _body je sdíleno !

};

private:

static char _dummy;

/* ... */

};

char String::_dummy = 0;

Page 105: Objektov ě-orientované p rogramování v C++

String č. 4 - operator [ ] - zápis do privátní kopie

class String {

public:

 

char & operator[]( int pos)

{

/* ... */

make_private();

return _body->buffer()[ _pos];

};

private:

void make_private()

{

if ( _body->count > 1 )

{

StringBody * nb = new StringBody( * _body); // copy-constructor

_body->dec();

_body = nb;

_body->inc();

}

};

Page 106: Objektov ě-orientované p rogramování v C++

String č. 4 - operator [ ] - čtení a zápis

class String {

public:

  char operator[]( int pos) const

{

/* ... */

return _body->buffer()[ _pos];

};

char & operator[]( int pos)

{

/* ... */

make_private();

return _body->buffer()[ _pos];

};

};

Page 107: Objektov ě-orientované p rogramování v C++

String č. 4 - operator [ ] - čtení a zápis

class String {

public:

  char operator[]( int pos) const

{

/* ... */

return _body->buffer()[ _pos];

};

char & operator[]( int pos)

{

/* ... */

make_private();

return _body->buffer()[ _pos];

};

};

String a, b; char c; const String d;

a[ 1] = c; // char & operator[]( int)

c = d[ 1]; // char operator[]( int) const

c = b[ 1]; // char & operator[]( int) - vytváří privátní kopii

Page 108: Objektov ě-orientované p rogramování v C++

String č. 4 - operator [ ] – rozlišení čtení a zápisuclass StringPos;

class String {public: /* ... */ char read_pos( int pos) const { /* ... */ } void write_pos( int pos, char b) { /* ... */ }

  char operator[]( int pos) const { return read_pos( pos); } StringPos operator[]( int pos) { return StringPos( this, pos); }};

class StringPos {public: StringPos( String * t, int p) : t_( t), p_( p) {} operator char() const { return t_->read_pos( p_); } const StringPos & operator =( char b) const { t_->write_pos( p_, b); return * this; }private: String * t_; int p_;};

Page 109: Objektov ě-orientované p rogramování v C++

ŠablonyŠablony

Templates

Page 110: Objektov ě-orientované p rogramování v C++

Šablony tříd - příklad

Definicetemplate< int n, class T> class Array {

T p[ n]; T dummy;

public:

T & operator[]( int x) { return x<n ? p[x] : dummy; }

};

PoužitíArray< 5, int> a;

Array< 7, int> b;

Array< 5 + 2, int> c;

Array< 3, Array< 7, int> > d;

a[ 3] = b[ 3];

a = b; // chyba !!!

b = c; // OK, implicitní copy-constructor

d[ 2][ 3] = 1;

Page 111: Objektov ě-orientované p rogramování v C++

Šablony tříd - definice

Šablona je generická třída parametrizovaná libovolným počtem formálních parametrů těchto druhů:

celé číslo – uvnitř šablony se chová jako konstanta, použitelná jako meze polí

ukazatel libovolného typu libovolný typ – deklarováno zápisem class T nebo typename T,

identifikátor formálního parametru se chová jako identifikátor typu, použitelný uvnitř šablony v libovolné deklaraci

Prefix definice šablonytemplate< formální-parametry>

lze použít před několika formami deklarací; oblastí platnosti formálních parametrů je celá prefixovaná deklarace

Page 112: Objektov ě-orientované p rogramování v C++

Šablony tříd - instanciace

Instanciace šablony: Šablonu lze použít jako typ pouze s explicitním uvedením skutečných parametrů odpovídajících druhů:

celé číslo: celočíselný konstantní výraz ukazatel: adresa globální nebo statické proměnné či funkce

kompatibilního typu libovolný typ – jméno typu či typová konstrukce (včetně jiné

instanciované šablony)

Užití instanciované šablony: Instanciované šablony jsou stejného typu, pokud jsou stejného

jména a jejich skutečné parametry obsahují stejné hodnoty konstantních výrazů, adresy stejných proměnných či funkcí a stejné typy

Page 113: Objektov ě-orientované p rogramování v C++

Šablony tříd – pravidla použití

Uvnitř těla šablony (nebo jako její předky) je možno užívat libovolné typy včetně:

Instancí jiných šablon Téže šablony s jinými argumenty Téže šablony se stejnými argumenty

• V tomto případě se argumenty mohou, ale nemusí opisovat

Ekvivalentní varianty šablony s copy-constructorem:template< class T> class X {

X( const X< T> &);

};

template< class T> class X {

X( const X &);

};

• Některé překladače (nesprávně) připouštějí i tuto variantutemplate< class T> class X {

X< T>( const X< T> &);

};

Page 114: Objektov ě-orientované p rogramování v C++

Šablony tříd – pravidla použití

Metody šablon mohou mít těla uvnitř třídy nebo vně Vně uvedená těla metod musejí být připojena k šabloně takto:

template< class T> void X< T>::f( int a, int b)

{ /* ... */ }

V kvalifikovaném jméně metody je nutné uvést patřičný seznam argumentů, tj. X< T>::f a nikoliv X::f

Těla metod musejí být viditelná z každého místa, kde jsou pro nějakou instanci šablony volána

Musejí tedy typicky být v témže hlavičkovém souboru jako sama šablona.

Uvedení těla metody vně třídy tedy u šablon typicky nic nepřináší, může být však vynuceno rekurzivními odkazy mezi šablonami apod.

Page 115: Objektov ě-orientované p rogramování v C++

Šablony tříd – triky

Dopředná deklarace šablony

template< class T> class X;

/* ... zde může být použito X s jakýmikoliv argumenty...

... pouze v kontextech,

kde kompilátor nepotřebuje znát tělo šablony ...

*/

template< class T> class X

{ /* ... */

};

Page 116: Objektov ě-orientované p rogramování v C++

Šablony funkcí

Šablona funkce je generická globální funkce prefixovaná konstrukcí

template< /*...formální parametry...*/>

se stejnými druhy formálních parametrů šablony jako u šablon tříd

Všechny formální argumenty prefixu funkční šablony by měly být užity v typech formálních parametrů funkce

Pokud nejsou, nefunguje níže uvedené automatické odvozování

Skutečné argumenty šablony funkce se při volání neuvádějí Kompilátor je odvozuje z typů skutečných parametrů funkce Mohou se ovšem uvést explicitně za jménem funkce

Page 117: Objektov ě-orientované p rogramování v C++

Šablony funkcí

Pod stejným identifikátorem může být deklarováno několik různých šablon funkce a navíc několik obyčejných funkcí.

Obyčejné funkce mají přednost před generickými

template< class T> T max( T a, T b)

{ return a < b ? b : a; };

char * max( char * a, char * b)

{ return strcmp( a, b) < 0 ? b : a; };

template< int n, class T> T max( Array< n, T> a)

{ /* ... */ }

Příklad ze standardních knihoven:template< class T> void swap( T & a, T & b)

{ T tmp(a); a = b; b = tmp; };

• K tomu řada chytřejších implementací swap pro některé třídy

Page 118: Objektov ě-orientované p rogramování v C++

Šablony - triky

Parciální specializace Deklarovanou šablonu lze pro určité kombinace parametrů

předefinovat jinak, než určuje její základní definicetemplate< int n> class Array< n, bool> { /* specializace pro pole typu bool */ };

Krajním případem parciální specializace je explicitní specializaceExplicitní specializace

template<> class Array< 32, bool> { /* ... */ };

Explicitní instanciace Překladač je možné donutit ke kompletní instanciaci šablony

template class Array< 128, char>;

Triky a zvyklosti: traits, policy classes,...

Později...

Page 119: Objektov ě-orientované p rogramování v C++

Standardní knihovny C++Standardní knihovny C++

Page 120: Objektov ě-orientované p rogramování v C++

namespace

Konstrukce namespace umožňuje uzavřít několik deklarací typů, tříd, globálních proměnných a funkcí do zvláštního prostoru jmen

Konstrukci namespace lze otevírat vícenásobněnamespace X {

typedef char * ptr;

ptr f( ptr a, ptr b);

};

namespace X {

ptr g();

};

Uzavřené identifikátory lze mimo tento prostor referencovat kvalifikovaným jménem

X::ptr v = X::g();

Celý prostor jmen lze rozbalit do aktuálního bloku nebo modulu konstrukcí:

using namespace X;

Page 121: Objektov ě-orientované p rogramování v C++

Standardní knihovny C++

V novějších implementacích má většina hlavičkových souborů dvě verze

Stará konvence – v budoucnu nebude podporována• soubor vector.h obsahuje šablonu vector

Nová konvence• soubor vector obsahuje šablonu vector uzavřenou do namespace std

• je tedy nutné používat identifikátor std::vector

Standardní knihovny C++ mají tyto hlavní součástiZákladní knihovny převzaté z C, podle nové konvence v

přejmenovaných souborechRozšířené C++ knihovnyiostream: Systém znakového a formátovaného vstupu a

výstupuSTL: Standard Template Library

Page 122: Objektov ě-orientované p rogramování v C++

Základní knihovny C a C++

• <assert.h> <cassert> - ladicí funkce (makro assert)

• <ctype.h> <cctype> - klasifikace znaků (isalpha, isspace, ...)

• <errno.h> <cerrno> - chybové kódy (ENOMEM, ...), proměnná errno

• <float.h> <cfloat> - vlastnosti a limity reálných typů (DBL_MAX, ...)

• <limits.h> <limits> <climits> - limity celočíselných typů (INT_MAX, ...)

• <locale.h> <locale> <clocale> - přizpůsobení národnímu prostředí

• <math.h> <cmath> - matematické funkce (sin, ...)

• <setjmp.h> <csetjmp> - meziprocedurální skoky (setjmp, longjmp)

• <signal.h> <csignal> - signály operačního systému

• <stdarg.h> <cstdarg> - makra pro funkce s proměnným počtem argumentů

• <stddef.h> <cstddef> - užitečné typy a konstanty (NULL)

• <stdio.h> <cstdio> - standardní a souborový vstup a výstup

• <stdlib.h> <cstdlib> - užitečné funkce (malloc, ...)

• <string.h> <cstring> - manipulace s řetězci (strcpy, ...)

• <time.h> <ctime> - konverze data a času

• <wchar.h> <cwchar> - 16-bitové řetězce (wchar_t)

• <wctype.h> <cwctype> - klasifikace 16-bitových znaků

Page 123: Objektov ě-orientované p rogramování v C++

Rozšířené knihovny pro C++

<bitset.h> <bitset> -- šablona pro bitové pole (bitset<N>) <complex.h> <complex> - komplexní čísla různé přesnosti

(complex<double>,...) <exception.h> <exception> - nastavení zpracování výjimek

(set_unexpected,...) <stdexcept.h> <stdexcept> - standardní výjimky (overflow_error,...) <valarray.h> <valarray> - šablony různě inteligentních polí

(valarray,...), vektorové operace (podpora paralelních výpočtů matematických funkcí)

Page 124: Objektov ě-orientované p rogramování v C++

STLSTL

Standard Template Library

Page 125: Objektov ě-orientované p rogramování v C++

STL

KontejneryPrefabrikáty základních datových strukturŠablony parametrizované typem ukládaného objektu

Všechny kontejnery pracují s kopiemi vkládaných hodnot Typ hodnot musí mít alespoň copy-constructor a destruktor Některé druhy kontejnerů či operací s nimi vyžadují i operator=

nebo konstruktor bez parametrů

Hodnoty se přidávají a odebírají metodami odpovídajícími druhu kontejneru

K hodnotám je možno přistupovat pomocí iterátoru, který reprezentuje inteligentní ukazatel dovnitř kontejneru

Prostřednictvím iterátoru je možno měnit uložené hodnoty

Page 126: Objektov ě-orientované p rogramování v C++

STL – Příklad

#include <deque>

typedef std::deque< int> my_deque;

my_deque the_deque;

the_deque.push_back( 1);

the_deque.push_back( 2);

the_deque.push_back( 3);

int x = the_deque.front(); // 1

the_deque.pop_front();

my_deque::iterator ib = the_deque.begin();

my_deque::iterator ie = the_deque.end();

for ( my_deque::iterator it = ib; it != ie; ++it)

{

*it = *it + 3;

}

int y = the_deque.back(); // 6

the_deque.pop_back()

int z = the_deque.back(); // 5

Page 127: Objektov ě-orientované p rogramování v C++

STL – Kontejnery

Sekvenční kontejnery<list.h> <list>

list - obousměrně vázaný seznam

<deque.h> <deque> deque - fronta s přidáváním a odebíráním z obou stran

<string.h> <string> basic_string - posloupnost ukončená terminátorem string = basic_string< char> - chytřejší implementace řetězce

<vector.h> <vector> vector - pole prvků s přidáváním zprava

Odvozené kontejnery <queue.h> <queue>

• queue - fronta

<stack.h> <stack>• stack - zásobník

Page 128: Objektov ě-orientované p rogramování v C++

STL – Kontejnery

Pomocné metody kontejneru

Test prázdnostibool empty() const

Počet prvkůsize_t size() const

nepoužívat pro testy prázdnosti

Page 129: Objektov ě-orientované p rogramování v C++

STL – Kontejnery

Metody kontejneru, vracející iterátoryOdkaz na začátek kontejneru - první platný prvek

iterator begin()

const_iterator begin() const

Odkaz za konec kontejneru - za poslední platný prvekiterator end()

const_iterator end() const

iterator a const_iterator jsou typy definované uvnitř kontejneru, zvané iterátory

přístupné konstrukcemi jako vector< int>::iterator vlastnosti iterátorů jsou mírně závislé na druhu kontejneru

Iterátor kontejneru obsahujícího typ T je třída s operátory definovanými tak, aby se chovala podobně jako ukazatel na typ T

Vytváří se tak iluze, že kontejner je pole

Page 130: Objektov ě-orientované p rogramování v C++

STL – Kontejnery

Operátory definované na iterátorechpřístup k prvku, na který iterátor ukazuje

T & iterator::operator *() const

const T & const_iterator::operator *() const

posouvání iterátoru směrem ke konci jednosměrný iterátor

iterator & iterator::operator++()

posouvání iterátoru směrem k začátku obousměrný iterátor

iterator & iterator::operator--()

libovolný posun iterátor s přímým přístupem

iterator operator+( iterator, int)

iterator operator-( iterator, int)

Page 131: Objektov ě-orientované p rogramování v C++

STL – Kontejnery

Metody kontejneru pro vkládání

iterator insert( iterator p, T x)

vsune (kopii) x před prvek, na který ukazuje iterátor p• vrací iterátor ukazující na vložený prvek

void insert( iterator p, int n, T x)

vsune n kopií x před prvek, na který ukazuje iterátor p

template< typename other_iterator>

void insert( iterator p, other_iterator b, other_iterator e)

před prvek, na který ukazuje iterátor p, vsune kopii posloupnosti prvků ohraničené iterátory b a e

• Tato posloupnost nesmí být uvnitř téhož kontejneru

• Tato posloupnost může být součástí jiného druhu kontejneru

je-li p == end(), vkládání připojuje na konec kontejneru všechny dříve existující iterátory odkazující na tento kontejner jsou

po použití insert neplatné, včetně p

Page 132: Objektov ě-orientované p rogramování v C++

STL – Kontejnery

Metody kontejneru pro odebírání

iterator erase( iterator p)

vyjme prvek, na který ukazuje iterátor p• p nesmí být rovno end()

• vrací iterátor ukazující na prvek za vyjmutým prvkem (nebo end())

iterator erase( iterator p, iterator e)

vyjme všechny prvky mezi p a e, včetně p a vyjma e• p nesmí být rovno end()

• vrací iterátor odkazující na prvek e (původní iterátor e nebude platný)

všechny iterátory odkazující na tento kontejner jsou po použití erase neplatné, včetně p a e

void clear()

{ erase( begin(), end()); }

Page 133: Objektov ě-orientované p rogramování v C++

STL – Kontejnery

Odvozené funkce kontejneru pro přístup k prvkůmPrvky na koncích

• list, deque, vector

• podmínka: ! empty()

T & front()

const T & front() const

{ return * begin(); }

T & back()

const T & back() const

{ return * --end(); }

Page 134: Objektov ě-orientované p rogramování v C++

STL – Kontejnery

Odvozené funkce kontejneru pro přístup k prvkůmPrvky uprostřed

• deque, vector, string

• podmínka: n < size()

T & at( int n)

T & operator[]( int n)

const T & at( int n) const

const T & operator[]( int n) const

{ return * ( begin() + n); }

Page 135: Objektov ě-orientované p rogramování v C++

STL – Kontejnery

Odvozené funkce manipulace s konci kontejneruPřidání jednotlivého prvku

void push_front( T x)

{ return insert( begin(), x); }

• list, deque

void push_back( T x)

{ return insert( end(), x); }

• list, deque, vector

Odebrání jednotlivého prvkuvoid pop_front()

{ return erase( begin()); }

• list, deque

void pop_back()

{ return erase( --end()); }

• list, deque, vector

Page 136: Objektov ě-orientované p rogramování v C++

STL - Kontejnerysložitost operace

na kontejneru s n prvky

list deque vector basic_string

přídání / odebrání jednoho prvku na začátku

push_front

pop_front

konstantní konstantní funkce neexistuje

funkce neexistuje

přídání / odebrání jednoho prvku na i-té pozici

insert

erase

konstantní min( i, n - i) n - i n - i

přídání / odebrání m prvků na i-té pozici

insert

erase

m

přesuny mezi seznamy (splice) jsou konstantní

m +min( i, n - i) m + n - i m + n - i

přídání / odebrání jednoho prvku na konci

push_back

pop_back

konstantní konstantní konstantní funkce neexistuje

nalezení i-tého prvku

begin() + i funkce neexistuje

konstantní konstantní konstantní

paměťová náročnost

kontejneru s prvky velikosti s

(s + K) * n

K řádově 16 B

q * s * n

q kolem 1.2

q * s * n

q kolem 1.2

q * s * n

q kolem 1.2

Page 137: Objektov ě-orientované p rogramování v C++

STL - Kontejnery

Asociativní kontejnery<set.h> <set>

set<T> - množina multiset<T> - množina s opakováním

<map.h> <map> map<K,T> - asociativní pole, tj. parciální zobrazení K -> T multimap<K,T> - relace s rychlým vyhledáváním podle klíče K

pair<A,B> - pomocná šablona uspořádané dvojice• s položkami first, second

Page 138: Objektov ě-orientované p rogramování v C++

STL - Kontejnery

Asociativní kontejnery vyžadují relaci uspořádání na klíčích Klíčem se rozumí první parametr šablony kontejneru Uspořádání se obvykle definuje operátorem < definovaným na typu

klíče• Pozor na konstrukce typu set< char *>

Uspořádání lze rovněž zadat přídavným parametrem šablony• Parametrem je funktor, tj. třída obsahující operator() se dvěma

parametry (porovnávanými objekty) typu klíče

Definované uspořádání nemusí být antisymetrická relace pokud platí

! (x < y) && ! (y < x)pak jsou prvky x a y považovány za ekvivalentní

Page 139: Objektov ě-orientované p rogramování v C++

STL - Kontejnery

Asociativní kontejnery - procházení

Asociativní kontejnery jsou uspořádány podle klíčůV pořadí podle tohoto uspořádání je lze procházet iterátory

Metody begin() a end() a operátory iterátorů mají stejný význam, jako u sekvenčních kontejnerů

Kontejnery map a multimap obsahují uspořádané dvojice pair< K, T>

Procházení celého asociativního kontejneru se užívá málokdy

Iterátory se častěji získávají vyhledáváním

Page 140: Objektov ě-orientované p rogramování v C++

STL - Kontejnery

Asociativní kontejnery - vyhledávání

iterator set::find( T x)

iterator multiset::find( T x)

iterator map::find( K x)

iterator multimap::find( K x)

pokud v kontejneru existuje prvek s klíčem ekvivalentním x:• vrací iterátor ukazující na první takový prvek

• multiset, multimap: další takové prvky jsou dostupné operací ++

jinak vrací end()

operace má logaritmickou složitost

Page 141: Objektov ě-orientované p rogramování v C++

STL - Kontejnery

Asociativní kontejnery - intervalové dotazy

iterator set::lower_bound( T x)

iterator multiset::lower_bound( T x)

iterator map::lower_bound( K x)

iterator multimap::lower_bound( K x)

vrací první prvek jehož klíč není menší než x, případně end()

iterator set::upper_bound( T x)

iterator multiset::upper_bound( T x)

iterator map::upper_bound( K x)

iterator multimap::upper_bound( K x)

vrací první prvek jehož klíč je větší než x, případně end()

dvojici takto získaných iterátorů lze využít v jiných funkcích téhož i jiného kontejneru

operace mají logaritmickou složitost

Page 142: Objektov ě-orientované p rogramování v C++

STL - Kontejnery

Asociativní kontejnery - vkládáníset a map

pair< iterator, bool> set::insert( T x)

pair< iterator, bool> map::insert( pair< K, T> x)

pokud prvek ekvivalentní x (resp. x.first) v kontejneru není: kopie x se vloží do kontejneru vrací pair< iterator, bool>( p, true) kde p je iterátor ukazující na

vložený prvek

pokud prvek ekvivalentní x (resp. x.first) v kontejneru již je: vrací pair< iterator, bool>( p, false) kde p je iterátor ukazující na

existující prvek ekvivalentní x

operace má logaritmickou složitostpo operaci budou dříve existující iterátory neplatné

Page 143: Objektov ě-orientované p rogramování v C++

STL - Kontejnery

Asociativní kontejnery - vkládánímultiset a multimap

iterator multiset::insert( T x)

iterator multimap::insert( pair< K, T> x)

kopie x se vloží do kontejneru vrací iterátor ukazující na vložený prvek

operace má logaritmickou složitostpo operaci budou dříve existující iterátory neplatné

Page 144: Objektov ě-orientované p rogramování v C++

STL - Kontejnery

Asociativní kontejnery - odebíránípodle klíče

size_type set::erase( T x)

size_type multiset::erase( T x)

size_type map::erase( K x)

size_type multimap::erase( K x)

odebere všechny prvky s klíčem ekvivalentním zadanému x vrací počet odebraných prvků

operace má logaritmickou složitost vzhledem k velikosti kontejneru plus lineární k počtu odebraných prvků

po operaci budou dříve existující iterátory neplatné

Page 145: Objektov ě-orientované p rogramování v C++

STL - Kontejnery

Asociativní kontejnery - odebíránípodle iterátoru - jeden prvek

void set::erase( iterator p)

void multiset::erase( iterator p)

void map::erase( iterator p)

void multimap::erase( iterator p)

odebere prvek na který odkazuje iterátor p• p nesmí být rovno end()

operace má konstantní složitost rozumí se amortizovaná složitost

po operaci budou dříve existující iterátory neplatné

Page 146: Objektov ě-orientované p rogramování v C++

STL - Kontejnery

Asociativní kontejnery - odebíránípodle iterátoru - interval

void set::erase( iterator p, iterator e)

void multiset::erase( iterator p, iterator e)

void map::erase( iterator p, iterator e)

void multimap::erase( iterator p, iterator e)

vyjme všechny prvky mezi p a e, včetně p a vyjma e• p nesmí být rovno end()

operace má složitost logaritmickou vůči velikosti kontejneru plus lineární vůči počtu odebraných prvků

po operaci budou dříve existující iterátory neplatné

Page 147: Objektov ě-orientované p rogramování v C++

STL - Kontejnery

map - operator [ ]

T & map::operator[]( K x)

{ return (*((insert(make_pair( x, T()))).first)).second; }

Vrátí referenci na hodnotovou (second) složku prvku s klíčem ekvivalentním x

Pokud takový prvek neexistuje, vytvoří jej• Jeho hodnotová složka bude T()

Tento operátor je možno používat pro vkládání, přepisování i čtení prvků kontejneru

Kontejner map se chová jako asociativní pole (perl, PHP,...) Pozor: To neplatí u sekvenčních kontejnerů

Po této operaci (i v případě čtení) mohou být všechny iterátory odkazující na tento kontejner neplatné

Page 148: Objektov ě-orientované p rogramování v C++

STL – Ostatní

<algorithm.h> <algorithm> - užitečné algoritmy (for_each, sort, next_permutation, ...)

<functional.h> <functional> - podpora funktorů <iterator.h> <iterator> - podpora iterátorů <memory.h> <memory> - alokátory pro kontejnery <numeric.h> <numeric> - jednoduchá matematika na prvcích

kontejnerů <utility.h> <utility> - pomocné konstrukce (pair,...)

Page 149: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Šablona funkce for_each <algorithm>

template<class InputIterator, class Function>

Function for_each(

InputIterator first,

InputIterator last,

Function f);

first a last jsou iterátory, určující procházený úsek nějakého kontejneru (všetně first, mimo last)

f je buďto globální funkce (ukazatel na funkci), nebo funktor, tj. třída obsahující operator()

Funkce f (případně metoda operator()) je zavolána na každý prvek v zadaném intervalu

prvek se předává jako * iterator, což může být odkazem funkce f tedy může modifikovat prvky seznamu

Page 150: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Šablona funkce for_each <algorithm>

template<class InputIterator, class Function>

Function for_each(

InputIterator first,

InputIterator last,

Function f)

{

for (; first != last; ++first)

f( * first);

return f;

}

Skutečná implementace může využívat specifických vlastností procházených kontejnerů

• Viz parciální specializace

• Bude tedy rychlejší, než ručně psaný for cyklus!

Takto napsanou šablonu lze zkompilovat pro jakékoliv f, na které lze aplikovat operátor (), tedy jak pro funkci, tak pro funktor

Page 151: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Použití funkce for_each

void my_function( double & x)

{

x += 1;

}

void increment( list< double> & c)

{

for_each( c.begin(), c.end(), my_function);

}

Page 152: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Použití funkce for_each

class my_functor {

public:

double v;

void operator()( double & x)

{ x += v; }

my_functor( double p) : v( p) {}

};

void add( list< double> & c, double value)

{

for_each( c.begin(), c.end(), my_functor( value));

}

Page 153: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Použití funkce for_each

class my_functor {

public:

double s;

void operator()( const double & x)

{ s += x; }

my_functor() : s( 0.0) {}

};

double sum( const list< double> & c)

{

my_functor f;

f = for_each( c.begin(), c.end(), f);

return f.s;

}

Page 154: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Použití funkce for_each

class my_functor {

public:

double s;

void operator()( const double & x)

{ s += x; }

my_functor() : s( 0.0) {}

};

double sum( const list< double> & c)

{

my_functor f;

for_each( c.begin(), c.end(), f);

return f.s;

}

Pozor: f se předává hodnotou - tato implementace vždy vrací 0.0

Page 155: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Použití funkce for_each

class my_functor {

public:

double s;

void operator()( const double & x)

{ s += x; }

my_functor() : s( 0.0) {}

};

double sum( const list< double> & c)

{

return for_each( c.begin(), c.end(), my_functor()).s;

}

Page 156: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Šablona funkce find <algorithm>

template<class InputIterator, class T>

InputIterator find(

InputIterator first,

InputIterator last,

const T & value)

{

for (; first != last; ++first)

if ( * first == value )

break;

return first;

}

Page 157: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Šablona funkce find_if <algorithm>

template<class InputIterator, class Predicate>

InputIterator find_if(

InputIterator first,

InputIterator last,

Predicate pred)

{

for (; first != last; ++first)

if ( pred( * first) )

break;

return first;

}

Predikát pred může být funkce nebo funktor

Page 158: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Přehled algoritmůPrůchod kontejnerem

for_each

Čtení kontejnerů find, find_if - první prvek s danou vlastností find_end - poslední výskyt druhé sekvence v první find_first_of - první výskyt některého prvku druhé sekvence v první adjacent_find - první prvek ekvivalentní sousednímu count, count_if - počet prvků s danou vlastností mismatch - první odlišnost dvou sekvencí equal - test shody dvou sekvencí search - první výskyt druhé sekvence v první search_n - první n-násobný výskyt dané hodnoty

Page 159: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Přehled algoritmůSwap

swap - výměna obsahu dvou objektů• Pomocí parciální/explicitní specializace bývá implementována rychleji,

než kopírování

Modifikace kontejnerů výměnou prvků swap_ranges - výměna obsahu dvou sekvencí (volá swap) iter_swap - výměna obsahu dvou jednoprvkových sekvencí

Modifikace kontejnerů permutací (voláním swap) partition, stable_partition - přemístění prvků s danou vlastností

dopředu random_shuffle - náhodná permutace dle zadaného náhodného

generátoru reverse - otočení posloupnosti rotate, rotate_copy - rotace prvků

Page 160: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Přehled algoritmůModifikace kontejnerů přiřazením

copy, copy_backward - kopie první sekvence do druhé transform - aplikace zadané unární/binární operace na každý prvek

první/první a druhé sekvence a zapsání výsledku do druhé/třetí sekvence

replace, replace_if - nahrazení prvků s danou vlastností jinou hodnotou

replace_copy, replace_copy_if - kopie s nahrazením fill, fill_n - naplnění sekvence danou hodnotou generate, generate_n - naplnění sekvence z daného generátoru

Modifikace kontejnerů odebráním remove, remove_if - smazání prvků s danou vlastností unique, unique_copy - smazání opakujících se sousedních prvků

• vhodné pro setříděné nebo asociativní kontejnery

Pozor: Tyto funkce neprodlužují ani nezkracují kontejner

Page 161: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Přehled algoritmůPozor: Algoritmy neprodlužují ani nezkracují kontejner

vector< int> a, b;

a.push_back( 1); a.push_back( 2); a.push_back( 3);

copy( a.begin(), a.end(), b.begin()); // ilegální použití

Pro tyto účely existují "vkládající iterátory" <iterator> obsahuje tyto funkce vracející iterátory

• back_inserter( K) - iterátor vkládající na konec kontejneru K

• front_inserter( K) - iterátor vkládající na začátek kontejneru K

• inserter( K, I) - iterátor vkládající před iterátor I do kontejneru K

tyto iterátory jsou pouze výstupní• lze je použít jako cíl ve funkcích typu copy

copy( a.begin(), a.end(), back_inserter( b));

Page 162: Objektov ě-orientované p rogramování v C++

STL – Algoritmy

Přehled algoritmů min, max - minimum a maximum ze dvou hodnot

Třídění a spol. sort, stable_sort - třídění partial_sort, partial_sort_copy, nth_element - polotovary třídění push_heap, pop_heap, make_heap, sort_heap - operace na haldě min_element, max_element lexicographical_compare next_permutation, prev_permutation

Operace na setříděných kontejnerech lower_bound, upper_bound, equal_range - hledání prvku binary_search - test na přítomnost prvku includes - test podmnožinovosti merge, inplace_merge - sjednocení s opakováním set_union, set_intersection - sjednocení, průnik set_difference, set_symmetric_difference - množinový rozdíl

Page 163: Objektov ě-orientované p rogramování v C++

iostreamiostream

vstupní a výstupní proudy

Page 164: Objektov ě-orientované p rogramování v C++

iostream

#include <iostream>

#include <iomanip>

using namespace std;

f()

{

int X;

double Y;

cin >> X >> Y;

cout << "X = " << hex << setw(4) << X << ", Y = " << Y << endl;

}

Manipulátory hex - šestnáctkový výpis, setw - počet míst

• platí pro daný stream (cout) trvale (do další změny)

endl - vloží oddělovač řádek

Page 165: Objektov ě-orientované p rogramování v C++

stringstream

#include <string>

#include <sstream>

#include <iomanip>

using namespace std;

string f( int a)

{

ostringstream x;

x << "a = " << a;

return x.str();

}

<sstream> - *stringstream – spolupracuje se std::string<strstream> - *strstream – spolupracuje s char *

Page 166: Objektov ě-orientované p rogramování v C++

iostream

Hlavičkové soubory <fstream.h> <fstream> - souborový vstup a výstup (ifstream,

ofstream, fstream) <iomanip.h> <iomanip> - manipulátory pro nastavení parametrů

formátovaného vstupu a výstupu (setw, setprecision, setfill, setbase, ...)

<ios.h> <ios> - základní funkce abstraktního souboru, základní nastavení formátu (hex, left, ...)

<iostream.h> <iostream> - standardní vstup a výstup (cin, cout, cerr)

<istream.h> <istream> - abstraktní vstupní a kombinované médium (istream, iostream)

<ostream.h> <ostream> - abstraktní výstupní médium (ostream) <sstream.h> <sstream> - vnitřní paměť jako médium (istringstream,

ostringstream, stringstream)

Page 167: Objektov ě-orientované p rogramování v C++

iostream

Abstraktní rozhraní

basic_...<T> jsou šablony T = char - 8-bitové znakové sady

• typedef: ios, istream, ostream, iostream, streambuf

T = wchar_t - 16-bitové znakové sady• typedef: wios, wistream, wostream, wiostream, wstreambuf

ios_base

basic_ios<T>

basic_istream<T> basic_ostream<T>

basic_iostream<T>

basic_streambuf<T>

nastavení formátovače

stav média

příméa formátovanéčtení

přímýa formátovaný

zápis

virtuální funkcečtení a zápisu

dědičnost

virtuální dědičnost

ukazatel

Page 168: Objektov ě-orientované p rogramování v C++

iostream

Abstraktní rozhraní

funkce, které potřebují zapisovat, dostávají basic_ostream<T> &

void zapis_neco( ostream & o)

{

o << neco;

}

ios_base

basic_ios<T>

basic_ostream<T>

nastavení formátovače

stav média

přímýa formátovaný

zápis

dědičnost

virtuální dědičnost

Page 169: Objektov ě-orientované p rogramování v C++

iostream

Abstraktní rozhraní

funkce, které potřebují číst, dostávají basic_istream<T> &• pozor: čtení modifikuje stream (posunuje ukazovátko)

void cti_neco( istream & i)

{

i >> neco;

}

ios_base

basic_ios<T>

basic_istream<T>

nastavení formátovače

stav média

příméa formátovanéčtení

dědičnost

virtuální dědičnost

ukazatel

Page 170: Objektov ě-orientované p rogramování v C++

iostream

Abstraktní rozhraní

funkce, které potřebují číst i zapisovat totéž médium,dostávají basic_iostream<T> &

• ukazovátko čtení a zápisu NENÍ společnévoid zmen_neco( iostream & io)

{ io.read( neco, N);

io.write( neco2, N);

}

ios_base

basic_ios<T>

basic_istream<T> basic_ostream<T>

basic_iostream<T>

nastavení formátovače

stav média

příméa formátovanéčtení

přímýa formátovaný

zápis

dědičnost

virtuální dědičnost

ukazatel

Page 171: Objektov ě-orientované p rogramování v C++

iostream

Abstraktní rozhraní

basic_iostream< T> obsahuje funkce čtení i zápisu nastavení formátu i stav média jsou společné pro čtení i zápis basic_ios< T> zde musí být pouze v jedné instanci dědění basic_ios< T> musí být virtuální

template< class T> class basic_istream : virtual public basic_ios< T>

ios_base

basic_ios<T>

basic_istream<T> basic_ostream<T>

basic_iostream<T>

nastavení formátovače

stav média

příméa formátovanéčtení

přímýa formátovaný

zápis

dědičnost

virtuální dědičnost

Page 172: Objektov ě-orientované p rogramování v C++

iostream

Médium

Konkrétní médium je implementováno jakopotomek třídy basic_streambuf<T>

Standardní knihovna C++ nabízí:• soubor (to, co umí OS, tedy včetně rour apod.)

• uložení v paměti

Lze implementovat vlastní (např. výstup do okna)

ios_base

basic_ios<T>

basic_istream<T> basic_ostream<T>

basic_iostream<T> basic_...buf<T>

basic_streambuf<T>

stav formátovače

stav média

příméa formátovanéčtení

přímýa formátovaný

zápis

virtuální funkcečtení a zápisu

médium,implementacečtení a zápisu

Page 173: Objektov ě-orientované p rogramování v C++

sstream

Paměťové médium <sstream>

Obálkové třídy ...stringstream<T> zařídí vznik basic_stringbuf<T> Umožňují přístup k médiu jako basic_string<T>

ios_base

basic_ios<T>

basic_istream<T> basic_ostream<T>

basic_iostream<T> basic_stringbuf<T>

basic_istringstream<T> basic_ostringstream<T>

basic_stringstream<T>

basic_streambuf<T>

stav formátovače

stav média

příméa formátovanéčtení

přímýa formátovaný

zápis

virtuální funkcečtení a zápisu

médium,implementacečtení a zápisu

str: přístup k médiu str: přístup k médiu

str: přístup k médiu

Page 174: Objektov ě-orientované p rogramování v C++

fstream

Souborové médium <fstream>, <iostream> (cin, cout, cerr)

Obálkové třídy ...fstream<T> zařídí vznik basic_filebuf<T> Soubor se otevírá(zavírá) metodou open(close) těchto tříd

ios_base

basic_ios<T>

basic_istream<T> basic_ostream<T>

basic_iostream<T> basic_filebuf<T>

basic_ifstream<T> basic_ofstream<T>

basic_fstream<T>

basic_streambuf<T>

stav formátovače

stav média

příméa formátovanéčtení

přímýa formátovaný

zápis

virtuální funkcečtení a zápisu

médium,implementacečtení a zápisu

open, close

open, close

open, closeOperační systém

Page 175: Objektov ě-orientované p rogramování v C++

iostream

ios: stav média good, eof, fail, bad - metody indikující stavy

istream/ostream - neformátované čtení/zápis read/write - čtení/zápis n znaků get/put - čtení/zápis jednotlivých znaků a čtení po řádkách seekg/seekp, tellg/tellp - posun ukazovátka, zjištění pozice

• funkce ...g manipulují s ukazovátkem pro čtení (get, istream)

• funkce ...p manipulují s ukazovátkem pro čtení (put, ostream)

• u některých médií nefunguje (roury)

Page 176: Objektov ě-orientované p rogramování v C++

iostream

formátované čteníbasic_istream<T> & operator>>( basic_istream<T> & s, D & x)

operátor přečte několik znaků ve tvaru určeném typem D naplní výstupní parametr x

formátovaný zápisbasic_ostream<T> & operator<<( basic_ostream<T> & s, D x)

operátor vypíše x jako několik znaků ve tvaru určeném typem D

Oba operátory vrací levý operand Tím je umožněno zřetězené použití

s << x << y << z;

je ekvivalentnís << x; s << y; s << z;

Page 177: Objektov ě-orientované p rogramování v C++

iostream

formátované čtení/zápisbasic_istream<T> & operator>>( basic_istream<T> & s, D & x)

basic_ostream<T> & operator<<( basic_ostream<T> & s, D x)

Knihovna istream/ostream implementuje operátory pro typy (unsigned) short, (unsigned) int, (unsigned) long - dec, hex, oct float, double, long double - desetinný a/nebo exponenciální tvar bool, void * - pro ladicí účely char/wchar_t - znak char * / wchar_t * - řetězec v C tvaru

Další typy lze dodefinovat (jako globální operátory) Standardní knihovny C++ je definují pro

• string/wstring - řetězec

• complex - komplexní číslo

Page 178: Objektov ě-orientované p rogramování v C++

iostream

Definování vlastních formátovacích operátorů

class Souradnice { public: int x, y; };

std::ostream & operator<<(

std::ostream & s, const Souradnice & a)

{

return s << '[' << a.x << ',' << a.y << ']';

}

Použití

Souradnice p;

std::cout << "p = " << p << std::endl;

Page 179: Objektov ě-orientované p rogramování v C++

iostream

Problém: namespace

namespace prostor {

class Souradnice { public: int x, y; };

std::ostream & operator<<(

std::ostream & s, const Souradnice & a)

{

return s << '[' << a.x << ',' << a.y << ']';

}

};

prostor::Souradnice p;

std::cout << p; // správný operator<< je v namespace prostor,

// který není přímo vidět

Page 180: Objektov ě-orientované p rogramování v C++

iostream

Problém: namespace

namespace prostor {

class Souradnice { public: int x, y; };

std::ostream & operator<<(

std::ostream & s, const Souradnice & a)

{

return s << '[' << a.x << ',' << a.y << ']';

}

};

prostor::Souradnice p;

std::cout << p; // správný operator<< je v namespace prostor,

// který není přímo vidět

std::cout << std::endl; // tentýž problém je ale už tady:

// tento operator<< je v namespace std

Page 181: Objektov ě-orientované p rogramování v C++

Koenig lookup

prostor::Souradnice p;

std::cout << p; // správný operator<< je v namespace prostor,

// který není přímo vidět

std::cout << std::endl; // tentýž problém je ale už tady:

// tento operator<< je v namespace std

Oba případy jsou překládány správněJe k tomu nutná složitá definice vyhledávání identifikátoru

tzv. Koenigovo vyhledávání používá se, je-li význam identifikátoru závislý na parametrech

• volání funkce

• použití operátoru

Page 182: Objektov ě-orientované p rogramování v C++

Koenig lookup

Koenigovo vyhledávání (zjednodušeno) Argument-dependent name lookup (ISO C++)

Pro každý skutečný parametr se z jeho typu T určí množina asociovaných namespace

Je-li T číselný, tyto množiny jsou prázdné Je-li T union nebo enum, jeho asociovaným namespace je ten, ve

kterém je definován Je-li T ukazatel na U nebo pole U, přejímá asociované namespace

od typu U Je-li T funkce nebo ukazatel na funkci, přejímá (sjednocením)

asociované namespace všech parametrů a návratového typu Je-li T třída, asociovanými namespace jsou ty, v nichž jsou

definovány tato třída a všichni její přímí i nepřímí předkové Je-li T instancí šablony, přejímá kromě asociovaných tříd a

namespace definovaných pro třídu také asociované třídy a namespace všech typových argumentů šablony

Page 183: Objektov ě-orientované p rogramování v C++

Koenig lookup

Koenigovo vyhledávání (zjednodušeno) Argument-dependent name lookup (ISO C++)

Pro každý skutečný parametr se z jeho typu T určí množina asociovaných namespace

Identifikátor funkce se pak vyhledává v těchto prostorech Globální prostor a aktuální namespace Všechny namespace přidané direktivami using Sjednocení asociovaných namespace všech parametrů funkce

Všechny varianty funkce nalezené v těchto namespace jsou rovnocenné

Mezi nimi se vybírá podle počtu a typu parametrů• Pokud není jednoznačně určena nejlepší varianta, je to chyba

Volání v kontextu třídy: Je-li identifikátor nalezen uvnitř této třídy nebo některého předka (jako metoda), má přednost před výše uvedenými variantami (globálními funkcemi)

Page 184: Objektov ě-orientované p rogramování v C++

iostream

Jak fungují manipulátory

cout << "X = " << hex << setw(4) << X << ", Y = " << Y << endl;

Bez parametrů (hex, endl, ...) Definovány jako funkce manipulující s ios_base

• Proto ios_base musí být třída a nikoliv šablona jako basic_ios

ios_base & hex( ios_base & s)

{ s.setf( ios_base::hex);

return s;

}

Akceptovány jako ukazatel na funkci zvláštní verzí operátoru <<

basic_ostream<T> & operator<<(

basic_ostream<T> & s, ios_base & (* f)( ios_base & s))

{ f( s);

return s;

}

Page 185: Objektov ě-orientované p rogramování v C++

iostream

Jak fungují manipulátory

cout << "X = " << hex << setw(4) << X << ", Y = " << Y << endl;

S parametry (setw, setprecision, setfill, ...) Definovány jako funkce vracející speciální třídu

struct setw_manip {

int x;

explicit setw_manip( int p) : x( p) {}

};

setw_manip setw( int p)

{ return setw_manip( p);

}

Akceptovány zvláštními verzemi operátoru <<basic_ostream<T> & operator<<(

basic_ostream<T> & s, const setw_manip & p)

{ s.width( p.x); return s;

}

Page 186: Objektov ě-orientované p rogramování v C++

iostream

Vztah proudů a kontejnerů

stringstream umožňuje přístup k médiu jako string

pro string jsou definovány operátory << a >>

<iterator> definuje iterátory nad proudy:istream_iterator< U>

vstupní iterátor, čte typ U pomocí operátoru >> parametrem konstruktoru je istream &

ostream_iterator< U>

výstupní iterátor, zapisuje typ U pomocí operátoru << parametrem konstruktoru je ostream &

Page 187: Objektov ě-orientované p rogramování v C++

iostream

Iterátory nad proudy - příkladynaplnění kontejneru ze vstupu

std::vector< double> a;

std::copy(

std::istream_iterator< double>( std::cin), // aktuální pozice

std::istream_iterator< double>(), // "konec souboru"

std::back_inserter( a)); // vkládací iterátor kontejneru

vysypání kontejneru na výstup nevýhoda: neodděluje elementy výstupu

std::vector< std::string> b;

std::copy(

b.begin(),

b.end(),

std::ostream_iterator< std::string>( std::cout));

Page 188: Objektov ě-orientované p rogramování v C++

Exception handlingException handling

Mechanismus výjimek

Page 189: Objektov ě-orientované p rogramování v C++

Exception handling

Srovnání: goto Start: příkaz goto Cíl: návěští

Určen při kompilaci Skok může opustit blok

Proměnné korektně zaniknouvoláním destruktorů

Cíl musí být v téže proceduře

int f()

{

if ( something == wrong )

{

goto label;

}

else

{

MyClass my_variable;

if ( anything != good )

{

goto label;

}

/* ... */

}

return 0;

label:

std::cerr

<< "Error"

<< std::endl;

return -1;

}

Page 190: Objektov ě-orientované p rogramování v C++

Exception handling

Srovnání: goto Start: příkaz goto Cíl: návěští

Určen při kompilaci Skok může opustit blok

Proměnné korektně zaniknouvoláním destruktorů

Cíl musí být v téže proceduře

Srovnání 2: <csetjmp> Pro pokročilé Start: volání longjmp Cíl: volání setjmp Skok může opustit proceduru Neřeší lokální proměnné

Nelze použít v C++ Předává hodnotu typu int

int f()

{

if ( something == wrong )

{

goto label;

}

else

{

MyClass my_variable;

if ( anything != good )

{

goto label;

}

/* ... */

}

return 0;

label:

std::cerr

<< "Error"

<< std::endl;

return -1;

}

Page 191: Objektov ě-orientované p rogramování v C++

Exception handling

Mechanismus výjimek Start: příkaz throw Cíl: try-catch blok

Určen za běhu Skok může opustit proceduru

Proměnné korektně zaniknouvoláním destruktorů

Předává hodnotu libovolného typu

Typ hodnoty se podílí na určení cíle skoku

void f()

{

if ( something == wrong )

throw 729;

else

{

MyClass my_variable;

if ( anything != good )

throw 123;

/* ... */

}

}

void g()

{

try {

f();

}

catch ( int e ) {

std::cerr

<< "Exception in f(): "

<< e

<< std::endl;

}

}

Page 192: Objektov ě-orientované p rogramování v C++

Exception handling

Mechanismus výjimek Start: příkaz throw Cíl: try-catch blok

Určen za běhu Skok může opustit proceduru

Proměnné korektně zaniknouvoláním destruktorů

Předává hodnotu libovolného typu

Typ hodnoty se podílí na určení cíle skoku

Obvykle se používají pro tento účel zhotovené třídy

class WrongException { /*...*/ };

class BadException { /*...*/ };

void f()

{

if ( something == wrong )

throw WrongException( something);

if ( anything != good )

throw BadException( anything);

}

void g()

{

try {

f();

}

catch ( const WrongException & e1 ) {

/*...*/

}

catch ( const BadException & e2 ) {

/*...*/

}

}

Page 193: Objektov ě-orientované p rogramování v C++

Exception handling

Mechanismus výjimek Start: příkaz throw Cíl: try-catch blok

Určen za běhu Skok může opustit proceduru

Proměnné korektně zaniknouvoláním destruktorů

Předává hodnotu libovolného typu

Typ hodnoty se podílí na určení cíle skoku

Obvykle se používají pro tento účel zhotovené třídy

Mechanismus výjimek respektuje hierarchii dědičnosti

class AnyException { /*...*/ };

class WrongException

: public AnyException { /*...*/ };

class BadException

: public AnyException { /*...*/ };

void f()

{

if ( something == wrong )

throw WrongException( something);

if ( anything != good )

throw BadException( anything);

}

void g()

{

try {

f();

}

catch ( const AnyException & e1 ) {

/*...*/

}

}

Page 194: Objektov ě-orientované p rogramování v C++

Exception handling

Mechanismus výjimek Start: příkaz throw Cíl: try-catch blok

Určen za běhu Skok může opustit proceduru

Proměnné korektně zaniknouvoláním destruktorů

Předává hodnotu libovolného typu

Typ hodnoty se podílí na určení cíle skoku

Obvykle se používají pro tento účel zhotovené třídy

Mechanismus výjimek respektuje hierarchii dědičnosti

Hodnotu není třeba využívat

class AnyException { /*...*/ };

class WrongException

: public AnyException { /*...*/ };

class BadException

: public AnyException { /*...*/ };

void f()

{

if ( something == wrong )

throw WrongException();

if ( anything != good )

throw BadException();

}

void g()

{

try {

f();

}

catch ( const AnyException &) {

/*...*/

}

}

Page 195: Objektov ě-orientované p rogramování v C++

Exception handling

Mechanismus výjimek Start: příkaz throw Cíl: try-catch blok

Určen za běhu Skok může opustit proceduru

Proměnné korektně zaniknouvoláním destruktorů

Předává hodnotu libovolného typu

Typ hodnoty se podílí na určení cíle skoku

Obvykle se používají pro tento účel zhotovené třídy

Mechanismus výjimek respektuje hierarchii dědičnosti

Hodnotu není třeba využívat Existuje univerzální catch blok

class AnyException { /*...*/ };

class WrongException

: public AnyException { /*...*/ };

class BadException

: public AnyException { /*...*/ };

void f()

{

if ( something == wrong )

throw WrongException();

if ( anything != good )

throw BadException();

}

void g()

{

try {

f();

}

catch (...) {

/*...*/

}

}

Page 196: Objektov ě-orientované p rogramování v C++

Exception handling

Fáze zpracování výjimkyVyhodnocení výrazu v příkaze throw

Hodnota je uložena "stranou"

Stack-unwinding Postupně se opouštějí bloky a funkce, ve kterých bylo provádění

vnořeno Na zanikající lokální a pomocné proměnné jsou volány destruktory Stack-unwinding končí dosažením try-bloku, za kterým je catch-blok

odpovídající typu výrazu v příkaze throw

Provedení kódu v catch-bloku Původní hodnota throw je stále uložena pro případné pokračování:

• Příkaz throw bez výrazu pokračuje ve zpracování téže výjimky počínaje dalším catch-blokem - začíná znovu stack-unwinding

Zpracování definitivně končí opuštěním catch-bloku Běžným způsobem nebo příkazy return, break, continue, goto

• Nebo vyvoláním jiné výjimky

Page 197: Objektov ě-orientované p rogramování v C++

Exception handling

Použití mechanismu výjimek

Vyvolání a zpracování výjimky je relativně časově náročné Používat pouze pro chybové nebo řídké stavy

• Např. nedostatek paměti, ztráta spojení, chybný vstup, konec souboru

Připravenost na výjimky také něco (málo) stojí Za normálního běhu je třeba zařídit, aby výjimka dokázala najít cíl a

zrušit proměnné• Výjimky se týkají i procedur, ve kterých není ani throw, ani try-blok

Většina kompilátorů umí překládat ve dvou režimech "s" a "bez"• Celý spojovaný program musí být přeložen stejně

Page 198: Objektov ě-orientované p rogramování v C++

Exception handling

Standardní výjimky<stdexcept> Všechny standardní výjimky jsou potomky třídy exception

metoda what() vrací řetězec s chybovým hlášením

bad_alloc: vyvolává operátor new při nedostatku paměti V režimu "bez výjimek" new vrací nulový ukazatel

bad_cast, bad_typeid: Chybné použití RTTIOdvozené z třídy logic_error:

domain_error, invalid_argument, length_error, out_of_range vyvolávány např. funkcí vector::operator[]

Odvozené z třídy runtime_error: range_error, overflow_error, underflow_error

Page 199: Objektov ě-orientované p rogramování v C++

Exception handling

Standardní výjimky<stdexcept> Všechny standardní výjimky jsou potomky třídy exception

metoda what() vrací řetězec s chybovým hlášením

bad_alloc: vyvolává operátor new při nedostatku paměti V režimu "bez výjimek" new vrací nulový ukazatel

bad_cast, bad_typeid: Chybné použití RTTIOdvozené z třídy logic_error:

domain_error, invalid_argument, length_error, out_of_range vyvolávány např. funkcí vector::operator[]

Odvozené z třídy runtime_error: range_error, overflow_error, underflow_error

Aritmetické ani ukazatelové operátory na vestavěných typech NEHLÁSÍ běhové chyby prostřednictvím výjimek

např. dělení nulou nebo dereference nulového ukazatele

Page 200: Objektov ě-orientované p rogramování v C++

Exception handling

Typické použití Deklarace výjimek

class AnyException { /*...*/ };

class WrongException

: public AnyException { /*...*/ };

class BadException

: public AnyException { /*...*/ };

Vyvolání výjimekvoid f()

{

if ( something == wrong )

throw WrongException();

if ( anything != good )

throw BadException();

}

Částečné ošetřenívoid g()

{ try {

f();

}

catch (...) {

std::cout << "Exception in g()";

throw;

}

}

Podrobné ošetřeníint main( /*...*/)

{ try {

g();

h();

}

catch ( WrongException ) {

std::cout << "WrongException";

}

catch ( BadException ) {

std::cout << "BadException";

}

}

Page 201: Objektov ě-orientované p rogramování v C++

Exception handling

Použití se std::exception Deklarace výjimek

class WrongException

: public std::exception {

virtual const char * what() const

{ return "WrongException"; }

};

class BadException

: public std::exception {

virtual const char * what() const

{ return "BadException"; }

};

Vyvolání výjimekvoid f()

{

if ( something == wrong )

throw WrongException();

if ( anything != good )

throw BadException();

}

Částečné ošetřenívoid g()

{ try {

f();

}

catch (...) {

std::cout << "Exception in g()";

throw;

}

}

Podrobné ošetřeníint main( /*...*/)

{ try {

g();

h();

}

catch ( const std::exception & e ) {

std::cout << e.what();

}

}

Page 202: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Používat throw a catch je jednoduché

Těžší je programovat běžný kód tak, aby se choval korektně i za přítomnosti výjimek

Exception-safety Exception-safe programming

void f()

{

int * a = new int[ 100];

int * b = new int[ 200];

g( a, b);

delete[] b;

delete[] a;

}

Pokud new int[ 200] způsobí výjimku, procedura zanechá naalokovaný nedostupný blok

Pokud výjimku vyvolá procedura g, zůstanou dva nedostupné bloky

Page 203: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Používat throw a catch je jednoduché

Těžší je programovat běžný kód tak, aby se choval korektně i za přítomnosti výjimek

Exception-safety Exception-safe programming

T & operator=( const T & b)

{

if ( this != & b )

{

delete body_;

body_ = new TBody( b.length());

copy( body_, b.body_);

}

return * this;

}

Pokud new TBody způsobí výjimku, operátor= zanechá v položce body_ původní ukazatel, který již míří na dealokovaný blok

Pokud výjimku vyvolá procedura copy, operátor zanechá třídu v neúplném stavu

Page 204: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Pravidla vynucená jazykem

Destruktor nesmí skončit vyvoláním výjimky Výjimka může být vyvolána uvnitř, ale musí být zachycena

nejpozději uvnitř destruktoru

Zdůvodnění: V rámci ošetření výjimek (ve fázi stack-unwinding) se volají

destruktory lokálních proměnných Výjimku zde vyvolanou nelze z technických i logických důvodů

ošetřit (ztratila by se původní výjimka) Nastane-li taková výjimka, volá se funkce terminate() a program

končí

Page 205: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Pravidla vynucená jazykem

Destruktor nesmí skončit vyvoláním výjimky Výjimka může být vyvolána uvnitř, ale musí být zachycena

nejpozději uvnitř destruktoru

Toto pravidlo jazyka sice platí pouze pro destruktory lokálních proměnných

A z jiných důvodů též pro globální proměnné

Je však vhodné je dodržovat vždy Bezpečnostní zdůvodnění: Destruktory lokálních proměnných často

volají jiné destruktory Logické zdůvodnění: Nesmrtelné objekty nechceme

Page 206: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Pravidla vynucená jazykem

Destruktor nesmí skončit vyvoláním výjimky

Konstruktor globálního objektu nesmí skončit vyvoláním výjimky

Zdůvodnění: Není místo, kde ji zachytit Stane-li se to, volá se terminate() a program končí Jiné konstruktory ale výjimky volat mohou (a bývá to vhodné)

Page 207: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Pravidla vynucená jazykem

Destruktor nesmí skončit vyvoláním výjimky

Konstruktor globálního objektu nesmí skončit vyvoláním výjimky

Copy-constructor typu v hlavičce catch-bloku nesmí skončit vyvoláním výjimky

Zdůvodnění: Catch blok by nebylo možné vyvolat Stane-li se to, volá se terminate() a program končí Jiné copy-constructory ale výjimky volat mohou (a bývá to vhodné)

Page 208: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Pravidla vynucená jazykem

Destruktor nesmí skončit vyvoláním výjimky

Konstruktor globálního objektu nesmí skončit vyvoláním výjimky

Copy-constructor typu v hlavičce catch-bloku nesmí skončit vyvoláním výjimky

Page 209: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Poznámka: Výjimky při zpracování výjimky

Výjimka při výpočtu výrazu v throw příkaze Tento throw příkaz nebude vyvolán

Výjimka v destruktoru při stack-unwinding Povolena, pokud neopustí destruktor Po zachycení a normálním ukončení destruktoru se pokračuje v

původní výjimce

Výjimka uvnitř catch-bloku Pokud je zachycena uvnitř, ošetření původní výjimky může dále

pokračovat (přikazem throw bez výrazu) Pokud není zachycena, namísto původní výjimky se pokračuje

ošetřováním nové

Page 210: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Kompilátory samy ošetřují některé výjimkyDynamická alokace polí

Dojde-li k výjimce v konstruktoru některého prvku, úspěšně zkonstruované prvky budou destruovány

• Ve zpracování výjimky se poté pokračuje

Page 211: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Kompilátory samy ošetřují některé výjimkyDynamická alokace polí

Dojde-li k výjimce v konstruktoru některého prvku, úspěšně zkonstruované prvky budou destruovány

• Ve zpracování výjimky se poté pokračuje

Výjimka v konstruktoru součásti (prvku nebo předka) třídy Sousední, již zkonstruované součásti, budou destruovány Ve zpracování výjimky se poté pokračuje

• Uvnitř konstruktoru je možno výjimku zachytit speciálním try-blokem:X::X( /* formální parametry */)

try : Y( /* parametry pro konstruktor součásti Y */)

{ /* vlastní tělo konstruktoru */

} catch ( /* parametr catch-bloku */ ) {

/* ošetření výjimky v konstruktoru Y i ve vlastním těle */

}

Konstrukci objektu nelze dokončit• Opuštění speciálního catch bloku znamená throw;

Page 212: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Definice

(Weak) exception safety Funkce (operátor, konstruktor) je (slabě) bezpečná, pokud i v

případě výjimky zanechá veškerá data v konzistentním stavu Konzistentní stav znamená zejména:

• Nedostupná data byla korektně destruována a odalokována

• Ukazatele nemíří na odalokovaná data

• Platí další invarianty dané logikou aplikace

Page 213: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Definice

(Weak) exception safety Funkce (operátor, konstruktor) je (slabě) bezpečná, pokud i v

případě výjimky zanechá veškerá data v konzistentním stavu Konzistentní stav znamená zejména:

• Nedostupná data byla korektně destruována a odalokována

• Ukazatele nemíří na odalokovaná data

• Platí další invarianty dané logikou aplikace

Strong exception safety Funkce je silně bezpečná, pokud v případě, že skončí vyvoláním

výjimky, zanechá data ve stejném stavu, ve kterém byla při jejím vyvolání

Nazýváno též "Commit-or-rollback semantics"

Page 214: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Poznámky

(Weak) exception safety Tohoto stupně bezpečnosti lze většinou dosáhnout Stačí vhodně definovat nějaký konzistentní stav, kterého lze vždy

dosáhnout, a ošetřit pomocí něj všechny výjimky• Konzistentním stavem může být třeba nulovost všech položek

• Je nutné upravit všechny funkce tak, aby je tento konzistentní stav nepřekvapil (mohou na něj ale reagovat výjimkou)

Strong exception safety Silné bezpečnosti nemusí jít vůbec dosáhnout, pokud je rozhraní

funkce navrženo špatně Obvykle jsou problémy s funkcemi s dvojím efektem

• Příklad: funkce pop vracející odebranou hodnotu

Page 215: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Nebezpečná implementace:

Pokud new char způsobí výjimku, operátor= zanechá v položce str_ původní ukazatel, který již míří na dealokovaný blok

class String {

public:

// ... 

private:

char * str_;

};

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

return * this;

}

Page 216: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Nebezpečná implementace:

Pokud new char způsobí výjimku, operátor= zanechá v položce str_ původní ukazatel, který již míří na dealokovaný blok

K jiné výjimce zde dojít nemůže:

• std::operator delete výjimky nikdy nevyvolává

• char je vestavěný typ a nemá tedy konstruktory které by mohly výjimku vyvolávat

• strlen a strcpy jsou C-funkce• Parametry a návratová

hodnota se předávají odkazem

class String {

public:

// ... 

private:

char * str_;

};

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

return * this;

}

Page 217: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator=

Naivní pokus o opravu: Pokud new char způsobí

výjimku, ošetří se Objekt se uvede do

konzistentního stavu Výjimka se propaguje dál - ven

z funkce

Problém: V catch bloku teoreticky může

vzniknout nová výjimka

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

try {

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

catch ( ... )

{

str_ = new char[ 1];

* str_ = 0;

throw;

}

}

return * this;

}

Page 218: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Lepší pokus o opravu:

Pokud new char způsobí výjimku, ošetří se

Je nutné pozměnit invariant třídy String:

Položka str_ nyní smí obsahovat nulový ukazatel

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

try {

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

catch ( ... )

{

str_ = 0;

throw;

}

}

return * this;

}

Page 219: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Lepší pokus o opravu:

Pokud new char způsobí výjimku, ošetří se

Je nutné pozměnit invariant třídy String:

Položka str_ nyní smí obsahovat nulový ukazatel

Takový exemplář String je považován za konzistentní

Konzistentnost nemusí znamenat, že to je z uživatelského pohledu platná hodnota

Může být považována i za chybovou a každá operace s takovou hodnotou může vyvolávat výjimku

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

try {

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

catch ( ... )

{

str_ = 0;

throw;

}

}

return * this;

}

Page 220: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Ekvivalentní řešení:

Nulovat str_ po delete Pokud new způsobí výjimku, v

str_ zůstane nulový ukazatel

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = 0;

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

return * this;

}

Page 221: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Chyba: změnili jsme invariant

str_ nyní může být nulové delete _str je v pořádku

• operator delete je vždy proti nulovému ukazateli ošetřen (nedělá nic)

strlen a strcpy ale fungovat nebudou

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = 0;

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

return * this;

}

Page 222: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Opraveno

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = 0;

if ( b.str_ )

{

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

}

return * this;

}

Page 223: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Vylepšení:

operator= může vyvolávat výjimku, pokud se přiřazuje neplatná hodnota

Tato výjimka může být definována např. takto:

#include <exception>

class InvalidString

: public std::exception

{

virtual const char * what() const

{ return "Invalid string";

}

}

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = 0;

if ( b.str_ )

{

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

else

{

throw InvalidString();

}

}

return * this;

}

Page 224: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Toto řešení je slabě bezpečné Silně bezpečné ale není:

Pokud dojde k výjimce, nezachovává se původní stav dat

To bude pro uživatele nepříjemné:

String x, y;

/* ... */

try { x = y + x;

}

catch (...) { /* ... */

}

Uživatel nedokáže rozlišit mezi výjimkami v operátorech + a =

Náš operator= ale v případě výjimky ztratí hodnotu x

String & String::operator=(

const String & b)

{

if ( this != & b )

{

delete[] str_;

str_ = 0;

if ( b.str_ )

{

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

else

{

throw InvalidString();

}

}

return * this;

}

Page 225: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Silně bezpečné řešení

Pokud dojde k výjimce v new, nestane se nic

Ani před throw nenastane žádná změna

String & String::operator=(

const String & b)

{

if ( this != & b )

{

if ( b.str_ )

{

char * aux = new char[

strlen( b.str_) + 1];

strcpy( aux, b.str_);

delete[] str_;

str_ = aux;

}

else

{

throw InvalidString();

}

}

return * this;

}

Page 226: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Silně bezpečné řešení Pozorování:

Toto řešení je "shodou okolností" imunní protithis == & b

String & String::operator=(

const String & b)

{

if ( this != & b )

{

if ( b.str_ )

{

char * aux = new char[

strlen( b.str_) + 1];

strcpy( aux, b.str_);

delete[] str_;

str_ = aux;

}

else

{

throw InvalidString();

}

}

return * this;

}

Page 227: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Silně bezpečné řešení Pozorování:

Toto řešení je "shodou okolností" imunní protithis == & b

Test je možno zrušit

String & String::operator=(

const String & b)

{

if ( b.str_ )

{

char * aux = new char[

strlen( b.str_) + 1];

strcpy( aux, b.str_);

delete[] str_;

str_ = aux;

}

else

{

throw InvalidString();

}

return * this;

}

Page 228: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Silně bezpečné řešení

Pokud je copy-constructor silně bezpečný

Standardní řešení: Copy-constructor naplní lokální

proměnnou c kopií parametru b• Zde může dojít k výjimce

Metoda swap vyměňuje obsah this a proměnné c

• Metoda swap je rychlá a nevyvolává výjimky

Před návratem z operatoru se volá destruktor c

• Tím zaniká původní obsah this

void String::swap( String & x)

{

char * aux = str_;

str_ = x.str_;

x.str_ = aux;

}

String & String::operator=(

const String & b)

{

String c( b);

swap( c);

return * this;

}

Page 229: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Silně bezpečné řešení

Metodu swap je vhodné publikovat ve formě globální funkce

Některé algoritmy nad kontejnery obsahujícími String se tak zrychlí a stanou se bezpečnými vůči výjimkám

void String::swap( String & x)

{

char * aux = str_;

str_ = x.str_;

x.str_ = aux;

}

String & String::operator=(

const String & b)

{

String c( b);

swap( c);

return * this;

}

void swap( String & x, String & y)

{

x.swap( y);

}

Page 230: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 operator= Silně bezpečné řešení

Metodu swap je vhodné publikovat ve formě globální funkce

Některé algoritmy nad kontejnery obsahujícími String se tak zrychlí a stanou se bezpečnými vůči výjimkám

Sama metoda swap může využívat šablonu swap pro typ char *

#include <algorithm>

void String::swap( String & x)

{

swap( str_, x.str_);

}

String & String::operator=(

const String & b)

{

String c( b);

swap( c);

return * this;

}

void swap( String & x, String & y)

{

x.swap( y);

}

Page 231: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: String č. 2 copy-constructor Silně bezpečné řešení

Pokud tělo dorazí na konec, budou datové položky korektně vyplněny

Tělo může vyvolávat výjimky• V takovém případě není třeba

datové položky vyplňovat• Objekt nebude považován za

platný a nebude používán ani destruován

Obecně je však třeba ošetřit try-blokem situace, kdy je v objektu více dynamicky alokovaných ukazatelů

String( const String & b)

{

if ( b.str_ )

{

str_ = new char[

strlen( b.str_) + 1];

strcpy( str_, b.str_);

}

else

{

throw InvalidString();

}

}

Page 232: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem

Slabě bezpečná implementace: Při výjimce v konstruktoru

proměnné s se nestane nic operator delete nezpůsobuje

výjimky

struct Box { String v; Box * next; };

class StringStack {

public:

// ... 

private:

Box * top_;

};

String StringStack::pop()

{

if ( ! top_ )

throw StackEmpty();

Box * p = top_;

String s = p->v;

top_ = p->next;

delete p;

return s;

}

Page 233: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem

Slabě bezpečná implementace Není silně bezpečná:

Funkce vrací hodnotou Pokud při vracení dojde k

výjimce v copy-constructoru, zásobník již bude zkrácen

struct Box { String v; Box * next; };

class StringStack {

public:

// ... 

private:

Box * top_;

};

String StringStack::pop()

{

if ( ! top_ )

throw StackEmpty();

Box * p = top_;

String s = p->v;

top_ = p->next;

delete p;

return s;

}

Page 234: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem

Slabě bezpečná implementace Není silně bezpečná:

Funkce vrací hodnotou Pokud při vracení dojde k

výjimce v copy-constructoru, zásobník již bude zkrácen

Tuto výjimku lze ošetřittry-blokem okolo příkazu return

• Uvést zásobník do původního stavu

• Ale: co když se uvedení do původního stavu nezdaří?

String StringStack::pop()

{

if ( ! top_ )

throw StackEmpty();

Box * p = top_;

String s = p->v;

top_ = p->next;

delete p;

try {

return s;

}

catch ( ...)

{

p = new Box;

p->v = s;

p->next = top_;

top_ = p;

throw;

}

}

Page 235: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem

Nefunkční implementace Není silně bezpečná:

Funkce vrací hodnotou Pokud při vracení dojde k

výjimce v copy-constructoru, zásobník již bude zkrácen

Tuto výjimku lze ošetřittry-blokem okolo příkazu return

Dokážeme udělat obnovení původního stavu bez nebezpečí výjimky

• Ale: jak zrušíme proměnnou p, když k výjimce nedojde?

String StringStack::pop()

{

if ( ! top_ )

throw StackEmpty();

Box * p = top_;

String s = p->v;

top_ = p->next;

// tady bylo delete p;

try {

return s;

// tady by delete p; nepomohlo

}

catch ( ...)

{

top_ = p;

throw;

}

}

Page 236: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem

Silně bezpečná implementace Jak zrušíme proměnnou p,

když k výjimce nedojde? std::auto_ptr< T>

"chytrý" ukazatel na T, který se chová jako "jediný vlastník objektu":

• po zkopírování se vynuluje • při zániku volá delete

Pozor: auto_ptr má nestandardní copy-constructor a operator=

• modifikují svůj parametr• pro auto_ptr nefungují

kontejnery apod.

#include <memory>

String StringStack::pop()

{

if ( ! top_ )

throw StackEmpty();

std::auto_ptr< Box> p = top_;

top_ = p->next;

try {

return p->v;

}

catch ( ...)

{

top_ = p;

// toto přiřazení nuluje p

throw;

}

}

// při návratu se automaticky zruší * p

// pokud je p nenulové

Page 237: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem

Silně bezpečná implementace Uživatel ji nedokáže použít tak,

aby to bylo silně bezpečné Vracenou hodnotu je nutné

okopírovat Nedá se poznat, zda výjimku

vyvolala metoda pop nebo operator=

• V prvním případě je zásobník nedotčen, ale ve druhém je již zkrácen

StringStack stk;

String a;

/* ... */

try {

a = stk.pop();

}

catch (...)

{

/* ??? */

}

Page 238: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem

Řešení A Jako v STL Rozdělit pop na dvě funkce

top vrací vrchol zásobníku• může jej vracet odkazem• nemodifikuje data

pop pouze zkracuje• je silně bezpečná

StringStack stk;

String a;

/* ... */

try {

a = stk.top();

}

catch (...)

{

/* chyba kopírování

nebo prázdný zásobník,

proměnná a nezměněna,

zásobník nedotčen */

}

try {

stk.pop();

}

catch (...)

{

/* chyba zkracování,

proměnná a změněna,

zásobník nedotčen */

}

Page 239: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem

Řešení B Namísto vracení hodnoty

funkce pop vyplňuje parametr předávaný odkazem

tím se vyloučí nutnost kombinovat volání pop s dalším kopírováním

Pro uživatele jednodušší, implementace pop je však těžší

StringStack stk;

String a;

/* ... */

try {

stk.pop( a);

}

catch (...)

{

/* chyba zkracování nebo kopírování,

proměnná a nezměněna,

zásobník nedotčen */

}

Page 240: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován seznamem

Řešení B Lze implementovat nad

řešením A

#include <memory>

class StringStack {

public:

/* A */

String & top();

void pop();

/* B */

void pop( String & out)

{

String & t = top();

swap( out, t);

try {

pop();

}

catch (...) {

swap( out, t);

throw;

}

}

};

Page 241: Objektov ě-orientované p rogramování v C++

Exception specifications

Exception specifications U každé funkce (operátoru,

metody) je možno určit seznam výjimek, kterými smí být ukončena

Na výjimky ošetřené uvnitř funkce se specifikace nevztahuje

Pokud není specifikace uvedena, povoleny jsou všechny výjimky

Specifikace respektuje dědičnost, to jest automaticky povoluje i všechny potomky uvedené třídy

void a()

{

/* tahle smí všechno */

}

void b() throw ()

{

/* tahle nesmí nic */

}

void c() throw ( std::bad_alloc)

{

/* tahle smí std::bad_alloc */

}

void d() throw ( std::exception, MyExc)

{

/* tahle smí potomky

std::exception a MyExc */

}

Page 242: Objektov ě-orientované p rogramování v C++

Exception specifications

Exception specificationsKompilátor zajistí, že nepovolená výjimka neopustí funkci:

Pokud by se tak mělo stát, volá se unexpected()• unexpected() smí vyvolat "náhradní" výjimku

Pokud ani náhradní výjimka není povolena, zkusí se vyvolat std::bad_exception

Pokud ani std::bad_exception není povoleno, volá se terminate() a program končí

Page 243: Objektov ě-orientované p rogramování v C++

Exception specifications

Exception specifications Kompilátor zajistí, že

nepovolená výjimka neopustí funkci

Toto je běhová kontrola Kompilátor smí vydávat nejvýše

varování Funkce smí volat jinou, která

by mohla vyvolat nepovolenou výjimku (ale nemusí)

void f() throw ( std::exception)

{

}

void g() throw ()

{

f(); /* tohle se smí */

}

Page 244: Objektov ě-orientované p rogramování v C++

Exception specifications

Exception specificationsKompilátor (a runtime) zajistí, že nepovolená výjimka

neopustí funkci Microsoft Visual C++ 7.0 to ovšem neimplementuje

Kompilátor to může využít Speciálně při volání funkce s prázdným throw () se nemusí

generovat ošetřující kód Program se zmenší a možná i zrychlí

Užitek pro programátory: Komentář Ladicí prostředek

Page 245: Objektov ě-orientované p rogramování v C++

CCastast

Různé druhy přetypování

Page 246: Objektov ě-orientované p rogramování v C++

Přetypování

Různé varianty syntaxeC-style cast

(T)e

Převzato z C

Page 247: Objektov ě-orientované p rogramování v C++

Přetypování

Různé varianty syntaxeC-style cast

(T)e

Převzato z C

Function-style castT(e)

Ekvivalentní (T)e• T musí být syntakticky identifikátor nebo klíčové slovo označující typ

Page 248: Objektov ě-orientované p rogramování v C++

Přetypování

Různé varianty syntaxeC-style cast

(T)e

Převzato z C

Function-style castT(e)

Ekvivalentní (T)e• T musí být syntakticky identifikátor nebo klíčové slovo označující typ

Type conversion operators Pro odlišení účelu (síly a nebezpečnosti) přetypování:

const_cast<T>(e)

static_cast<T>(e)

reinterpret_cast<T>(e)

Page 249: Objektov ě-orientované p rogramování v C++

Přetypování

Různé varianty syntaxeC-style cast

(T)e

Převzato z C

Function-style castT(e)

Ekvivalentní (T)e• T musí být syntakticky identifikátor nebo klíčové slovo označující typ

Type conversion operators Pro odlišení účelu (síly a nebezpečnosti) přetypování:

const_cast<T>(e)

static_cast<T>(e)

reinterpret_cast<T>(e)

Novinka - přetypování s běhovou kontrolou:dynamic_cast<T>(e)

Page 250: Objektov ě-orientované p rogramování v C++

Přetypování

const_cast<T>(e)

Odstranění konstantnosti const U & => U & const U * => U *

Obvykle používáno pro měnění pomocných položek v logicky konstantních objektech

Příklad: Čítač odkazů na logicky konstantní objektclass Data {

public:

void register_pointer() const

{ const_cast< Data *>( this)->references++; }

private:

/* ... data ... */

int references;

};

Jiný příklad: datové struktury s amortizovaným vyhledáváním

Page 251: Objektov ě-orientované p rogramování v C++

Přetypování

const_cast<T>(e)

Odstranění konstantnosti const U & => U & const U * => U *

U moderních překladačů lze nahradit specifikátorem mutable

Příklad: Čítač odkazů na logicky konstantní objektclass Data {

public:

void register_pointer() const

{ references++; }

private:

/* ... data ... */

mutable int references;

};

Page 252: Objektov ě-orientované p rogramování v C++

Přetypování

static_cast<T>(e)

UmožňujeVšechny implicitní konverze

Bezztrátové i ztrátové aritmetické konverze (int <=> double apod.) Konverze přidávající modifikátory const a volatile Konverze ukazatele na void * Konverze odkazu na odvozenou třídu na odkaz na předka:

• Derived & => Base &

• Derived * => Base *

Aplikace copy-constructoru; v kombinaci s implicitní konverzí též:• Derived => Base (slicing: okopírování části objektu)

Aplikace libovolného konstruktoru T::T s jedním parametrem• Uživatelská konverze libovolného typu na třídu T

Aplikace konverzního operátoru: operator T()• Uživatelská konverze nějaké třídy na libovolný typ T

Page 253: Objektov ě-orientované p rogramování v C++

Přetypování

static_cast<T>(e)

UmožňujeVšechny implicitní konverze

Ekvivalentní použití pomocné proměnné tmp deklarované takto:T tmp(e);

Používá se tehdy, pokud se vynucením jedné z možných implicitních konverzí odstraní nejednoznačnost nebo vynutí volání jiné varianty funkce

class A { /* ... */ }; class B { /* ... */ };

void f( A *); void f( B*);

class C : public A, public B { /* ... */

void g() { f( static_cast< A>( this)); }

};

Page 254: Objektov ě-orientované p rogramování v C++

Přetypování

static_cast<T>(e)

UmožňujeVšechny implicitní konverzeKonverze na void - zahození hodnoty výrazu

Používá se v makrech a podmíněných výrazech

Konverze odkazu na předka na odkaz na odvozenou třídu• Base & => Derived &

• Base * => Derived *

Pokud objekt, na nějž konvertovaný odkaz ukazuje, není typu Derived či z něj odvozený, je výsledek nedefinovaný

• K chybě obvykle dojde později!

Konverze celého čísla na výčtový typ Pokud hodnota čísla neodpovídá žádné výčtové konstantě,

výsledek je nedefinovaný

Konverze void * na libovolný ukazatel

Page 255: Objektov ě-orientované p rogramování v C++

Přetypování

static_cast<T>(e)

Nejčastější použitíKonverze odkazu na předka na odkaz na odvozenou třídu

class Base { public: enum Type { T_X, T_Y };

virtual Type get_type() const = 0;

};

class X : public Base { /* ... */

virtual Type get_type() const { return T_X; }

};

class Y : public Base { /* ... */

virtual Type get_type() const { return T_Y; }

};

Base * p = /* ... */;

switch ( p->get_type() ) {

case T_X: { X * xp = static_cast< X *>( p); /* ... */ } break;

case T_Y: { Y * yp = static_cast< Y *>( p); /* ... */ } break;

}

Page 256: Objektov ě-orientované p rogramování v C++

Přetypování

reinterpret_cast<T>(e)

UmožňujeKonverze ukazatele na dostatečně velké celé čísloKonverze celého čísla na ukazatelKonverze mezi různými ukazateli na funkceKonverze odkazu na odkaz na libovolný jiný typ

• U * => V *

• U & => U &

Neuvažuje příbuzenské vztahy tříd, neopravuje hodnoty ukazatelů

Většina použití je závislá na platformě Příklad: Přístup k reálné proměnné po bajtech Typické použití: Čtení a zápis binárních souborů

void put_double( std::ostream & o, const double & d)

{ o.write( reinterpret_cast< char *>( & d), sizeof( double)); }

• Obsah souboru je nepřenositelný

Page 257: Objektov ě-orientované p rogramování v C++

Přetypování

dynamic_cast<T>(e)

UmožňujeKonverze odkazu na odvozenou třídu na odkaz na předka:

• Derived & => Base &

• Derived * => Base *

Implicitní konverze, chová se stejně jako static_cast

Konverze odkazu na předka na odkaz na odvozenou třídu• Base & => Derived &

• Base * => Derived *

Podmínka: Base musí obsahovat alespoň jednu virtuální funkci Pokud konvertovaný odkaz neodkazuje na objekt typu Derived nebo

z něj odvozený, je chování definováno takto:• Konverze ukazatelů vrací nulový ukazatel

• Konverze referencí vyvolává výjimku std::bad_cast

Umožňuje přetypování i v případě virtuální dědičnosti

Page 258: Objektov ě-orientované p rogramování v C++

Přetypování

dynamic_cast<T>(e)

Nejčastější použitíKonverze odkazu na předka na odkaz na odvozenou třídu

class Base { public:

virtual ~Base(); /* alespoň jedna virtuální funkce */

};

class X : public Base { /* ... */

};

class Y : public Base { /* ... */

};

Base * p = /* ... */;

X * xp = dynamic_cast< X *>( p);

if ( xp ) { /* ... */ }

Y * yp = dynamic_cast< Y *>( p);

if ( yp ) { /* ... */ }

Page 259: Objektov ě-orientované p rogramování v C++

RTTIRTTI

Typová informace za běhu

Page 260: Objektov ě-orientované p rogramování v C++

RTTI

Operátor typeidtypeid(T)

Vrací identifikaci typu Ttypeid(e)

Pokud výraz e je typu reference na třídu s alespoň jednou virtuální funkcí

• Vrací identifikaci typu objektu určeného výrazem e

• Pokud je reference nulová, vyvolává výjimku std::bad_typeid

Jinak• Vrací identifikaci statického typu výrazu e

Identifikace typuconst std::type_info &

<typeinfo> Lze porovnávat na rovnost Má metodu name() vracející řetězec s nějakou formou jména typu

Page 261: Objektov ě-orientované p rogramování v C++

RTTI

Typické použitíAlternativa místo dynamic_cast

#include <typeinfo>

class Base { public:

virtual ~Base(); /* alespoň jedna virtuální funkce */

};

class X : public Base { /* ... */

};

class Y : public Base { /* ... */

};

Base * p = /* ... */;

if ( typeid( * p) == typeid( X) )

{ X * xp = static_cast< X *>( p); /* ... */ }

if ( typeid( * p) == typeid( Y) )

{ Y * yp = static_cast< Y *>( p); /* ... */ }

Page 262: Objektov ě-orientované p rogramování v C++

RTTI

Typické použitíAlternativa místo dynamic_cast

Pozor: rovnost typeid nereflektuje dědičnost• Zatímco dynamic_cast ano

#include <typeinfo>

class Base { public:

virtual ~Base(); /* alespoň jedna virtuální funkce */

};

class X : public Base { /* ... */

};

class Z : public X { /* ... */

};

Base * p = new Z;

if ( typeid( * p) == typeid( X) ) // neplatí !

{ X * xp = static_cast< X *>( p); /* ... */ }

if ( typeid( * p) == typeid( Z) ) // platí

{ Z * zp = static_cast< Z *>( p); /* ... */ }

Page 263: Objektov ě-orientované p rogramování v C++

RTTI

RTTI obvykle něco stojí Každá třída s virtuálními funkcemi někde musí mít své type_info a

odkaz na něj ve své tabulce virtuálních funkcí• To platí i pro instance šablon, jejichž jména bývají dlouhá

RTTI se příliš nevyužívá Je slabší než dynamic_cast Je nové

• Mezitím se programátoři naučili dělat si RTTI vlastními prostředky

Většina překladačů zapíná RTTI na vyžádání Někdy se takový přepínač vztahuje i na dynamic_cast U některých překladačů souvisí s RTTI i zpracování výjimek

Page 264: Objektov ě-orientované p rogramování v C++

Kanonické tvary třídKanonické tvary tříd

Page 265: Objektov ě-orientované p rogramování v C++

Kanonické tvary tříd

Neinstanciované třídyPolicy classesTraits templates

Třídy s jednou instancíSingletony

Třídy instanciované jako proměnnéUživatelské datové typy, chytré ukazatele, ...Funktory a podobné parametry šablonVisitory a podobné objekty s virtuálními funkcemi

Třídy instanciované dynamickyPlain Old DataVelká a sdílená dataTřídy s hierarchií dědičnosti

Poznámka: Toto třídění nemůže být ani úplné ani jednoznačné

Page 266: Objektov ě-orientované p rogramování v C++

Kanonické tvary tříd

Neinstanciované třídy Policy classes Traits templates

Obsahují pouze Definice typů (typedef,

případně vnořené třídy) Definice výčtových konstant (a

typů) Statické funkce Statická data

Obvykle vše veřejné (struct)

Nemají konstruktory ani destruktory

Obvykle nevyužívají dědičnost

struct allocation_policy {

static void * alloc( size_t);

static void free( void *);

};

template< typename T>

struct type_traits;

template<>

struct type_traits< char> {

typedef char param_type;

enum { min = 0, max = 255 };

static bool less( char, char);

};

Page 267: Objektov ě-orientované p rogramování v C++

Kanonické tvary tříd

Policy class – použití Univerzální šablona

template< class policy> class BigTree {

/* ... */

Node * new_node()

{ return policy::alloc(sizeof(Node));

}

};

Specifická policy class

struct my_policy {

static void * alloc( size_t);

static void free( void *);

};

Použití

BigTree< my_policy> my_tree;

Traits template – použití Deklarace traits

template< class T>

struct type_traits;

Univerzální šablonatemplate< class T> class Stack {

/* ... */

void push(

typename type_traits::param_t x);

};

Univerzální definice traitstemplate< class T>

struct type_traits {

typedef const T & param_t;

};

Explicitní specializace traitstemplate<>

struct type_traits< char> {

typedef char param_t;

};

Page 268: Objektov ě-orientované p rogramování v C++

Kanonické tvary tříd

Třídy s jednou instancí Singletony

Privátní konstruktor a destruktor

Znemožní vytváření a likvidaci mimo předepsaná místa

Privátní hlavička copy-constructoru a operatoru=

Tělo se neimplementuje Znemožní kopírování

Privátní statická proměnná odkazující na instanci

Veřejná statická funkce zpřístupňující instanci

Většinou nevyužívají dědičnost ani virtuální funkce

/* syslog.h */

class SysLog {

public:

static SysLog & instance()

{ if ( ! pinst_ )

pinst_ = new SysLog;

return * pinst_;

}

void write( const char * s)

{ ::write( s, handle_); }

private:

int handle_;

SysLog() { handle_=::open("syslog");}

~SysLog() { ::close(handle_); }

SysLog( const SysLog &);

void operator=( const SysLog &);

static std::auto_ptr<SysLog*> pinst_;

};

/* syslog.cpp */

std::auto_ptr<SysLog*> SysLog::pinst_;

Page 269: Objektov ě-orientované p rogramování v C++

Kanonické tvary tříd

Třídy instanciované jako proměnné

Jednoduché datové typy Neobsahují dynamicky

alokovaná data

Konstruktor bez parametrů Zajišťuje definovanou iniciální

hodnotu Copy-constructor, operator=,

destruktor se nedefinují Implementace kompilátorem

vyhovuje

Unární a modifikující binární operátory jako metody

Nemodifikující binární operátory jako globální funkce

class Complex {

public:

double re, im;

Complex();

Complex operator-() const;

Complex & operator+=(

const Complex &);

};

Complex operator+(

const Complex &, const Complex &);

Page 270: Objektov ě-orientované p rogramování v C++

Kanonické tvary tříd

Třídy instanciované jako proměnné

Složitější datové typy

Konstruktor bez parametrů Copy-constructor operator= Destruktor

Unární a modifikující binární operátory jako metody

Nemodifikující binární operátory jako globální funkce

Obvykle musejí být friend Data a pomocné funkce

privátní

Dědičnost nemá smysl

class Matrix {

public:

Matrix();

Matrix( const Matrix &);

Matrix & operator=( const Matrix &);

~Matrix();

Matrix operator-() const;

Matrix & operator+=( const Matrix &);

friend Matrix operator+(

const Matrix &, const Matrix &);

private:

int m_, n_; double * d_;

};

Matrix operator+(

const Matrix &, const Matrix &);

Page 271: Objektov ě-orientované p rogramování v C++

Kanonické tvary tříd

Třídy instanciované jako proměnné

Funktory Třídy určené k předávání

šablonovaným funkcím Obvykle používány pouze

jednou• Co nejjednodušší konstrukce

Konstruktor S požadovanými parametry

Data

Metoda implementující požadovanou operaci

Typicky operator()

Dědičnost nemá smysl

struct Printer {

public:

Printer( std::ostream & o)

: out_( o) {}

void operator()( int x)

{ o << x << std::endl; }

private:

std::ostream & out_;

};

std::vector< int> v;

for_each( v.begin(), v.end(),

Printer( std::cout));

Page 272: Objektov ě-orientované p rogramování v C++

Visitor

Visitor Abstraktní třída

Určena k předání jiné funkci, která vybere a zavolá vhodnou virtuální metodu podle skutečného typu nějakého objektu

Často používána ve spojení s procházením polymorfní datovou strukturou

Příklad Datová struktura Scene

obsahuje objekty tří druhů (Ellipse, Rectangle, Line)

Metoda doitforall projde všechny prvky scény a aplikuje na každý z nich vhodnou virtuální metodu zadaného Visitoru

class Ellipse;

class Rectangle;

class Line;

class Visitor {

public:

virtual void visitEllipse(

Ellipse *)=0;

virtual void visitRectangle(

Rectangle *)=0;

virtual void visitLine(

Line *)=0;

virtual ~Visitor() {}

};

class Scene {

public:

void doitforall( Visitor &);

/* ... */

};

Page 273: Objektov ě-orientované p rogramování v C++

Visitor

Visitor - použití

class PrintVisitor : public Visitor {

public:

PrintVisitor( Printer * p)

: p_( p) {}

virtual void visitEllipse(

Ellipse *);

virtual void visitRectangle(

Rectangle *);

virtual void visitLine(

Line *);

private:

Printer * p_;

};

Scene s;

Printer * p;

s.doitforall( PrintVisitor( p));

class Ellipse;

class Rectangle;

class Line;

class Visitor {

public:

virtual void visitEllipse(

Ellipse *)=0;

virtual void visitRectangle(

Rectangle *)=0;

virtual void visitLine(

Line *)=0;

virtual ~Visitor() {}

};

class Scene {

public:

void doitforall( Visitor &);

/* ... */

};

Page 274: Objektov ě-orientované p rogramování v C++

Visitor

Visitor Visitor umožňuje definovat tolik

variant nějaké akce, kolik je druhů procházených objektů

Pomocí visitoru je možné definovat další akce

Příklad Vyskytne-li se potřeba výstupu

na plotter, stačí definovat další potomek visitoru

class Ellipse;

class Rectangle;

class Line;

class Visitor {

public:

virtual void visitEllipse(

Ellipse *)=0;

virtual void visitRectangle(

Rectangle *)=0;

virtual void visitLine(

Line *)=0;

virtual ~Visitor() {}

};

class Scene {

public:

void doitforall( Visitor &);

/* ... */

};

Page 275: Objektov ě-orientované p rogramování v C++

Visitor

Srovnání Virtuální funkce

Podobného efektu jako u visitoru lze dosáhnout virtuální funkcí deklarovanou ve společném předku objektů s odlišnými těly pro každý druh objektu

Sada virtuálních funkcí ve společném předku není rozšiřitelná

class AbstractObject {

public:

virtual void print(

Printer *)=0;

virtual void display(

Display *)=0;

virtual ~AbstractObject() {}

};

class Ellipse : public AbstractObject

{ /* ... */ };

class Ellipse;

class Rectangle;

class Line;

class Visitor {

public:

virtual void visitEllipse(

Ellipse *)=0;

virtual void visitRectangle(

Rectangle *)=0;

virtual void visitLine(

Line *)=0;

virtual ~Visitor() {}

};

class Scene {

public:

void doitforall( Visitor &);

/* ... */

};

Page 276: Objektov ě-orientované p rogramování v C++

Visitor

Visitor Výhoda: Přidávání další akce

nevyžaduje změnu společného rozhraní

Nevýhoda: Přidání dalšího druhu objektu si vynutí změnu visitoru (přidání virtuální funkce)

Poučení V případě stabilní množiny akcí

na nestabilní množině druhů objektů použijte virtuální funkce

V případě nestabilní množiny akcí na stabilní množině druhů objektů použijte visitor

V případě nestabilní množiny akcí i druhů ... ?

class Ellipse;

class Rectangle;

class Line;

class Visitor {

public:

virtual void visitEllipse(

Ellipse *)=0;

virtual void visitRectangle(

Rectangle *)=0;

virtual void visitLine(

Line *)=0;

virtual ~Visitor() {}

};

class Scene {

public:

void doitforall( Visitor &);

/* ... */

};

Page 277: Objektov ě-orientované p rogramování v C++

Visitor

Visitor - implementace

class AbstractObject {

public:

virtual void apply( Visitor &) = 0;

virtual ~AbstractObject() {}

};

class Ellipse {

protected:

virtual void apply( Visitor & v)

{ v.visitEllipse( v); }

/* ... */

};

void Scene::doitforall( Visitor & v)

{

for (

my_vector_::iterator it =

elements_.begin();

it != elements_.end(); ++it )

(*it)->apply( v);

}

class Ellipse;

class Rectangle;

class Line;

class Visitor {

public:

virtual void visitEllipse(

Ellipse *)=0;

virtual void visitRectangle(

Rectangle *)=0;

virtual void visitLine(

Line *)=0;

virtual ~Visitor() {}

};

class Scene {

public:

void doitforall( Visitor &);

/* ... */

private:

typedef vector< AbstractObject *>

my_vector_;

my_vector_ elements_;

};

Page 278: Objektov ě-orientované p rogramování v C++

Kanonické tvary tříd

Třídy instanciované jako proměnné

Abstraktní visitor Sada čistě virtuálních funkcí Virtuální destruktor

• Prázdné tělo

Vše veřejné

Konkrétní visitor Obvykle používán pouze

jednou• Co nejjednodušší konstrukce• Kontrola přístupu není nutná

Potomek abstraktní třídy Privátní nebo veřejná data Konstruktor (jsou-li data) Virtuální metody implementující

požadované operace

class Visitor {

public:

virtual void visitEllipse( Ellipse *)=0;

virtual void visitRectangle( Rectangle *)=0;

virtual void visitLine( Line *)=0;

virtual ~Visitor() {}

};

class PrintVisitor : public Visitor {

public:

PrintVisitor( Printer * p)

: p_( p) {}

virtual void visitEllipse(

Ellipse *);

virtual void visitRectangle(

Rectangle *);

virtual void visitLine(

Line *);

private:

Printer * p_;

};

Page 279: Objektov ě-orientované p rogramování v C++

Kanonické tvary tříd

Třídy instanciované dynamicky

Plain Old Data Neobsahují dynamicky

alokované součásti

Datové položky obvykle veřejné Většinou bez konstruktoru Nedefinuje se copy-constructor,

operator= ani destruktor Bez virtuálních funkcí a

dědičnosti Někdy s obyčejnými metodami

class Configuration {

public:

bool show_toolbar;

bool show_status_bar;

int max_windows;

};

Pro srovnání Pojem "Plain Old Data" (POD)

je definován jazykem C++ takto:

Třída nemá žádný konstruktor ani destruktor

Třída nemá virtuální funkce ani virtuální dědičnost

Všichni předkové a datové položky jsou POD

POD-třída má zjednodušená pravidla:

Může být součástí unie Může být staticky inicializována

Page 280: Objektov ě-orientované p rogramování v C++

Kanonické tvary tříd

Třídy instanciované dynamicky

Velká a sdílená data Často obsahuje definice typů Konstruktor nebo několik

konstruktorů, často s parametry Destruktor Privátní datové položky Manipulace prostřednictvím

metod udržujících konzistenci Obvykle privátní

neimplementovaný copy-constructor a operator=

Většinou bez virtuálních funkcí a dědičnosti

class ColoredGraph {

public:

typedef int node_id;

typedef int edge_id;

typedef int color;

ColoredGraph();

ColoredGraph( istream &);

~ColoredGraph();

node_id add_node();

edge_id add_edge(

node_id, node_id, color);

/* ... */

private:

vector< node_id> nodes_;

multimap< node_id, edge_id> edges_;

map< edge_id, color> colors_;

ColoredGraph(const ColoredGraph &);

void operator=(const ColoredGraph &);

};

Page 281: Objektov ě-orientované p rogramování v C++

Kanonické tvary tříd

Třídy instanciované dynamicky

Třídy s hierarchií dědičnosti "Objektové programování"

Abstraktní třída Sada veřejných (čistě)

virtuálních funkcí Veřejný virtuální destruktor

• Prázdné tělo

Konstruktor obvykle protected• Chrání proti instanciaci, pokud

nejsou čistě virtuální funkce

Pro jistotu: privátní neimplementovaný copy-constructor a operator=

• Kopírování metodou clone

Žádné datové položky

class AbstractObject {

public:

virtual void doit() {}

virtual void

showit( Where *) const = 0;

virtual AbstractObject *

clone() const = 0;

virtual ~AbstractObject() {}

protected:

AbstractObject() {}

private:

AbstractObject(

const AbstractObject&);

void operator=(

const AbstractObject&);

};

Page 282: Objektov ě-orientované p rogramování v C++

Kanonické tvary tříd

Třídy instanciované dynamicky

Třídy s hierarchií dědičnosti "Objektové programování"

Konkrétní třída Potomek abstraktní třídy Veřejný konstruktor Virtuální funkce obvykle

protected Privátní data

class ConcreteObject

: public AbstractObject {

public:

ConcreteObject( /*...*/);

protected:

virtual void doit();

virtual void showit( Where *) const;

virtual AbstractObject *

clone() const;

virtual ~ConcreteObject();

private:

/* data */;

};

Page 283: Objektov ě-orientované p rogramování v C++

Design PatternsDesign Patterns

Návrhové vzory

Page 284: Objektov ě-orientované p rogramování v C++

Design patterns

Design patterns

Abstract factory Adapter Bridge Builder Chain of responsibility Command Composite Decorator Facade Factory method Flyweight Generation gap Interpreter Iterator

Mediator Memento Multicast Observer Prototype Proxy Singleton State Strategy Template method Typed message Visitor

a mnoho dalších...

Gamma, Helm, Johnson, Vlissides: Design Patterns, 1995

Page 285: Objektov ě-orientované p rogramování v C++

Design patterns

Design patterns - proč?

Vše již bylo vynalezeno - nevynalézat kolo

Moudrost věků - neopakovat chyby

Výuka a poučení - jak to dělají jiní

Zpřístupnění objektového programování "méně kreativním" programátorům

Společná terminologie

Ekvivalenty v různých jazycích (C#, Java, CORBA, ...)

Page 286: Objektov ě-orientované p rogramování v C++

Design patterns

Design patterns

Původním záměrem je jejich používání při návrhu Je to návod, případně vzorová implementace Není to knihovna ani polotovar k úpravě

Klasické návrhové vzory nevyužívají C++ šablony Ani "template function" není šablona

Některé (ne všechny) vzory lze univerzálně implementovat jako šablony

Zkušenost je obsažena především v pravidlech určujících, kdy je vhodné daný vzor použít

V originále: "Intent" + "Applicability"

Page 287: Objektov ě-orientované p rogramování v C++

Design patterns

Struktura Adapter

• Objekt upravující rozhraní objektu

Bridge• Oddělení implementace objektu od jeho rozhraní

Composite• Rozhraní umožňující jednotný přístup k celkům i částem určité

hierarchické struktury

Decorator• Třída podílející se na implementaci objektu

Facade• Objekt reprezentující část rozhraní objektu

Flyweight• Objekt snižující objem dat pomocí sdílení

Proxy• Objekt zpřístupňující jiný objekt se stejným rozhraním

Page 288: Objektov ě-orientované p rogramování v C++

Design patterns

Vytváření objektů Abstract factory

• Vytváření rodin objektů bez určení jejich konkrétních tříd

Builder• Vytváření složených objektů

Factory method• Vytváření objektu bez určení konkrétní třídy

Prototype• Objekt se schopností vytvářet vlastní kopie

Singleton• Třída s jedinou instancí a globálním přístupem k ní

Page 289: Objektov ě-orientované p rogramování v C++

Design patterns

Stav a synchronizace Iterator

• Objekt umožňující procházení datovou strukturou

Mediator• Objekt evidující vztahy jiných objektů

Memento• Záznam vnitřního stavu objektu určený k pozdějšímu obnovení

Observer• Synchronizace objektu a jeho pozorovatele

State• Oddělení funkcí a stavu objektu

Page 290: Objektov ě-orientované p rogramování v C++

Design patterns

Zprávy a příkazy Command

• Zhmotněný pokyn k volání funkce/metody s parametry

Interpreter• Převodník zpráv

Multicast• Předávání zpráv dynamicky registrovaným příjemcům

Typed message• Typově bezpečné předávání zpráv různého druhu

Visitor• Rozšiřitelná náhrada virtuální funkce

Page 291: Objektov ě-orientované p rogramování v C++

Design patterns

Implementace funkcí Chain of responsibility

• Soustava funkcí podílejících se na implementaci jedné akce

Generation gap• Úpravy chování neupravitelné třídy

Strategy• Objekt určený ke specializaci univerzálního postupu

Template method• Univerzální algoritmus s modifikovatelnými částmi

Page 292: Objektov ě-orientované p rogramování v C++

ŠablonyŠablony

Hlubší pohled

Page 293: Objektov ě-orientované p rogramování v C++

Šablony tříd – triky

Specializace metody pro konkrétní typ Překrývá generické tělo

template< class T> class X {

/* ... */

void f(/*...*/);

};

template< class T> void X< T>::f(/*...*/)

{ /*... generické tělo pro libovolný typ T ... */}

template<> void X< int>::f(/*...*/)

{ /*... speciální tělo pro typ int ... */}

Page 294: Objektov ě-orientované p rogramování v C++

Šablony tříd – triky

Specializace metody pro konkrétní typ Překrývá generické tělo

template<> void X< int>::f(/*...*/)

{ /*... speciální tělo pro typ int ... */}

Starší verze C++ zde nepoužívají prefix template<>

void X< int>::f(/*...*/)

{ /*... speciální tělo pro typ int ... */}

Page 295: Objektov ě-orientované p rogramování v C++

Šablony tříd – triky

Specializace metody pro konkrétní typ Překrývá generické tělo

template<> void X< int>::f(/*...*/)

{ /*... speciální tělo pro typ int ... */}

Tato konstrukce se nazývá Explicitní specializace šablony funkce

Page 296: Objektov ě-orientované p rogramování v C++

Šablony tříd – triky

Specializace metody pro konkrétní typ Překrývá generické tělo

template<> void X< int>::f(/*...*/)

{ /*... speciální tělo pro typ int ... */}

Tato konstrukce se nazývá Explicitní specializace šablony funkce

Existují též: Explicitní specializace celé třídy Parciální specializace funkce nebo celé třídy Explicitní instanciace

Později...

Page 297: Objektov ě-orientované p rogramování v C++

Šablony tříd – triky

Specializace metody pro konkrétní typ Překrývá generické tělo

template<> void X< int>::f(/*...*/)

{ /*... speciální tělo pro typ int ... */}

Tato konstrukce se nazývá Explicitní specializace šablony funkce

Existují též: Explicitní specializace celé třídy Parciální specializace celé třídy Explicitní instanciace

Později...

Page 298: Objektov ě-orientované p rogramování v C++

Parciální specializace

Parciální specializace Deklarovanou šablonu lze pro určité kombinace parametrů

předefinovat jinak, než určuje její základní definice• Parciálně specializovat lze šablony funkcí, celé šablony tříd i jednotlivě

těla jejich metod

• Obsah specializace šablony třídy (teoreticky) nemusí nijak souviset se základní definicí - může mít zcela jiné položky, předky apod.

• Základní definice dokonce nemusí vůbec existovat (ale musí být deklarována)

template< class X, class Y> class C; // základní deklarace

template< class P, class Q> class C< P *, Q *> { // specializace

bool cmp( P *, Q *);

};

template< class Z> class C< Z, Z> : public Z { // jiná specializace

bool set( Z &);

};

template< class X, class Y> class C { // základní definice

X add( Y);

};

Page 299: Objektov ě-orientované p rogramování v C++

Parciální specializace

Parciální specializace Deklarovanou šablonu lze pro určité kombinace parametrů

předefinovat jinak, než určuje její základní definice• Parciální specializace může mít stejný, menší i větší počet formálních

parametrů než základní definice, jejich hodnoty se odvozují ze skutečných parametrů šablony (kterých je vždy tolik, kolik určuje základní definice)

template< class T, class U, int n> class C< T[n], U[n]>

{ /* specializace pro dvě pole stejné velikosti */ };

Page 300: Objektov ě-orientované p rogramování v C++

Parciální specializace

Parciální specializace Deklarovanou šablonu lze pro určité kombinace parametrů

předefinovat jinak, než určuje její základní definice• Parciální specializace může mít stejný, menší i větší počet formálních

parametrů než základní definice, jejich hodnoty se odvozují ze skutečných parametrů šablony (kterých je vždy tolik, kolik určuje základní definice)

template< class T, class U, int n> class C< T[n], U[n]>

{ /* specializace pro dvě pole stejné velikosti */ };

Krajním případem parciální specializace je explicitní specializace

Explicitní specializace template<> class C< char, int[ 8]> { /* ... */ };

Explicitní specializace šablony není šablona Podléhá trochu jiným (jednodušším) pravidlům

• Překlad se neodkládá

• Těla metod se nepíší do hlavičkových souborů

Page 301: Objektov ě-orientované p rogramování v C++

Parciální specializace

Typická použití parciální a explicitní specializaceVýhodnější implementace ve speciálních případech

Šablona je používána přímo z nešablonovaného kódu Programátor - uživatel šablony o specializaci nemusí vědět Příklad: Implementace vector<char> může být jednodušší

Page 302: Objektov ě-orientované p rogramování v C++

Parciální specializace

Typická použití parciální a explicitní specializaceVýhodnější implementace ve speciálních případech

Šablona je používána přímo z nešablonovaného kódu Programátor - uživatel šablony o specializaci nemusí vědět Příklad: Implementace vector<char> může být jednodušší

Mírná změna rozhraní ve speciálních případech Šablona je používána přímo z nešablonovaného kódu Uživatel by měl být o specializaci informován Příklad: vector< bool> nedovoluje vytvořit ukazatel na jeden prvek

Page 303: Objektov ě-orientované p rogramování v C++

Parciální specializace

Typická použití parciální a explicitní specializaceVýhodnější implementace ve speciálních případech

Šablona je používána přímo z nešablonovaného kódu Programátor - uživatel šablony o specializaci nemusí vědět Příklad: Implementace vector<char> může být jednodušší

Mírná změna rozhraní ve speciálních případech Šablona je používána přímo z nešablonovaného kódu Uživatel by měl být o specializaci informován Příklad: vector< bool> nedovoluje vytvořit ukazatel na jeden prvek

Modifikace chování jiné šablony Specializovaná šablona je volána z jiného šablonovaného kódu Autor volající šablony předpokládá, že ke specializaci dojde

• Někdy dokonce ani nedefinuje základní obsah volané šablony

Autor specializace tak upravuje chování volající šablony Příklad: šablona basic_string<T> volá šablonu char_traits<T>, ve

které je např. definována porovnávací funkce

Page 304: Objektov ě-orientované p rogramování v C++

Parciální specializace

Modifikace chování jiné šablony Specializovaná šablona je volána z jiného šablonovaného kódu Autor volající šablony předpokládá, že ke specializaci dojde

• Někdy dokonce ani nedefinuje základní obsah volané šablony

Autor specializace tak upravuje chování volající šablony Příklad: šablona basic_string<T> volá šablonu char_traits<T>, ve

které je např. definována porovnávací funkce

template< class T> struct char_traits;

template< class T> class basic_string { /* ... */

int compare( const basic_string & b) const

{ /*...*/ char_traits< T>::compare( /* ... */) /*...*/ }

};

template<> struct char_traits< char> { /* ... */

static int compare(const char* s1, const char* s2, size_t n)

{ return memcmp( s1, s2, n); }

};

Page 305: Objektov ě-orientované p rogramování v C++

Parciální specializace

Modifikace chování jiné šablony Specializovaná šablona je volána z jiného šablonovaného kódu Autor volající šablony předpokládá, že ke specializaci dojde

• Někdy dokonce ani nedefinuje základní obsah volané šablony

Autor specializace tak upravuje chování volající šablony

TraitsŠablony, ze kterých nejsou vytvářeny objektyObsahují pouze:

Definice typů Statické funkce

Určeny k doplnění informací o nějakém typu Příklad: char_traits<T> doplňuje informace o typu T, např.

porovnávací funkci

Page 306: Objektov ě-orientované p rogramování v C++

Traits & policies

TraitsŠablony, ze kterých nejsou vytvářeny objektyObsahují pouze:

Definice typů Statické funkce

Určeny k doplnění informací o nějakém typu Příklad: char_traits<T> doplňuje informace o typu T, např.

porovnávací funkci

Policy classesTřídy, ze kterých obvykle nejsou vytvářeny objektyPředávány jako parametr šablonám

Defaultní hodnotou parametru často bývá šablona traits

Určeny k definování určitého chování Příklad: Alokační strategie

Page 307: Objektov ě-orientované p rogramování v C++

typename

Důsledky specializací Různé specializace téže šablony mohou mít naprosto odlišná těla Odkazuje-li se jedna šablona na jinou, nelze bez znalosti jejích

parametrů rozhodnout, které identifikátory jsou deklarovány a jak• Příklad: při kompilaci basic_string<T> není známo, co znamená

char_traits< T>::compare

Problém se týká tzv. závislých jmen, obsahujících jméno parametru šablony

• Příklady: T::x, C<T>, C<T>::x

K úspěšné syntaktické analýze je v C++ nutné odlišit jména typů od ostatních jmen:

bflm< T>::psvz( * x);

• volání statické funkce psvz nebo deklarace ukazatele na typ psvz?

V těle šablony je nutno všechna závislá kvalifikovaná jména označující typy opatřit klíčovým slovem typename

typename bflm< T>::psvz( * x); // deklarace

• Nesmí se používat v deklaraci předků šablony

• Nesmí se používat mimo šablony

Page 308: Objektov ě-orientované p rogramování v C++

Triky s šablonami

Porovnání typů s booleovským výstupemtemplate< class A, class B>

struct Equal {

enum { value = false };

};

template< class A>

struct Equal< A, A> {

enum { value = true };

};

Equal< X, Y>::value je konstantní výraz

Použitítemplate< class T1>

class Test {

enum { T1_is_int = Equal< int, T1>::value};

enum { T1_is_long = Equal< long, T1>::value};

/* ... */

};

Page 309: Objektov ě-orientované p rogramování v C++

Triky s šablonami

Porovnání typů s typovým výstupemtemplate< class A, class B, class C, class D>

struct IfEqual {

typedef D Result;

};

template< class A, class C, class D>

struct Equal< A, A, C, D> {

typedef C Result;

};

IfEqual< X, Y, U, V>::Result je typ• Význam: X == Y ? U : V

Použitítemplate< class T1>

class Test {

typedef Equal< T1, unsigned, unsigned long, long>::Result longT1;

/* ... */

};

Page 310: Objektov ě-orientované p rogramování v C++

Triky s šablonami

Kompilační ověření invariantutemplate< int x>

struct AssertNot;

template<>

struct AssertNot< 0> {

enum { value = true };

};

template< int x>

struct Assert {

enum { value = AssertNot< ! x>::value };

};

Page 311: Objektov ě-orientované p rogramování v C++

Triky s šablonami

Kompilační ověření invariantutemplate< int x>

struct AssertNot;

template<>

struct AssertNot< 0> {

enum { value = true };

};

template< int x>

struct Assert {

enum { value = AssertNot< ! x>::value };

};

Použitítemplate< int N> class Array {

enum { check = Assert< (N > 0)>::value };

/* ... */

};

Array< -3> x;

• error C2027: use of undefined type 'AssertNot<x>' with [x=1]

Page 312: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Šablona třídy je kompilátorem vyhodnocovaná funkce f : Ti T

T je množina všech typů zkonstruovatelných v jazyce C

Page 313: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Šablona třídy je kompilátorem vyhodnocovaná funkce f : Ti T

T je množina všech typů zkonstruovatelných v jazyce C

f : Ti × Kj T šablona s celočíselnými parametry K je množina všech celočíselných konstant

Page 314: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Šablona třídy je kompilátorem vyhodnocovaná funkce f : Ti T

T je množina všech typů zkonstruovatelných v jazyce C

f : Ti × Kj T šablona s celočíselnými parametry K je množina všech celočíselných konstant

f : Ti × Kj Tm × Kn

výsledná třída může obsahovat typy a konstanty

Page 315: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Šablona třídy je kompilátorem vyhodnocovaná funkce f : Ti × Kj Tm × Kn

Taková funkce může být definovánaJedním předpisem

Základní šablonou

Po částech Parciálními specializacemi šablony

V jednotlivých bodech Explicitními specializacemi šablony

Page 316: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Příklad

template< int N>

struct Fact {

enum { value = Fact< N-1>::value * N };

};

template<>

struct Fact< 0> {

enum { value = 1 };

};

Page 317: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Příklad

template< int N>

struct Fact {

enum { value = Fact< N-1>::value * N };

};

template<>

struct Fact< 0> {

enum { value = 1 };

};

Použití

enum { N = 10 };

int permutations[ Fact< N>::value];

Page 318: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Příklad

template< int N>

struct Fact {

enum { value = Fact< N-1>::value * N };

};

template<>

struct Fact< 0> {

enum { value = 1 };

};

Kontrolní otázka: Kolik je Fact< -1>::value

Page 319: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Příklad

template< int N>

struct Fact {

enum { value = Fact< N-1>::value * N };

};

template<>

struct Fact< 0> {

enum { value = 1 };

};

Kontrolní otázka: Kolik je Fact< -1>::value MS Visual C++ 7.1:

• fatal error C1202: recursive type or function dependency context too complex

Řetěz instanciací Fact< -1>, Fact< -2>, Fact< -3>, ... způsobí přetečení tabulek kompilátoru

Page 320: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Jiný příklad

template< int N>

struct Fib {

enum { value = Fib< N-1>::value + Fib< N-2>::value };

};

template<> struct Fib< 0> {

enum { value = 1 };

};

template<> struct Fib< 1> {

enum { value = 1 };

};

Kontrolní otázka: Jak dlouho trvá výpočet (tj. kompilace) Fib< 1000>::value

Page 321: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Jiný příklad

template< int N>

struct Fib {

enum { value = Fib< N-1>::value + Fib< N-2>::value };

};

template<> struct Fib< 0> {

enum { value = 1 };

};

template<> struct Fib< 1> {

enum { value = 1 };

};

Kontrolní otázka: Jak dlouho trvá výpočet (tj. kompilace) Fib< 1000>::value MS Visual C++ 7.1: Build Time 0:00 Kompilátory ukládají již vytvořené instanciace a nepočítají je znovu

Page 322: Objektov ě-orientované p rogramování v C++

******Nepoužité slajdyNepoužité slajdy******

Page 323: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Triky s typovým konstrukcemiSeznam typů

template< class H, class R>

struct List {

typedef H Head; typedef R Rest;

};

struct EmptyList {};

Použití

typedef List< char *,

List< const char *,

List< std::string,

EmptyList> > > StringTypes;

Page 324: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Triky s typovým konstrukcemiSeznam typů

template< class H, class R>

struct List {

typedef H Head; typedef R Rest;

};

struct EmptyList {};

Jiné použití

struct Apple {}; struct Pear {}; struct Plum {};

typedef List< Apple,

List< Pear,

List< Plum,

EmptyList> > > Fruits;

Page 325: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Triky s typovým konstrukcemiSeznam typů

template< class H, class R>

struct List {

typedef H Head; typedef R Rest;

};

struct EmptyList {};

Funkce na seznamu typůtemplate< class L> struct First {

typedef typename L::Head Result;

};

Page 326: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Triky s typovým konstrukcemiSeznam typů

template< class H, class R>

struct List {

typedef H Head; typedef R Rest;

};

struct EmptyList {};

Funkce na seznamu typůtemplate< class L> struct First {

typedef typename L::Head Result;

};

struct NullType {};

template<> struct First< EmptyList> {

typedef NullType Result;

};

Page 327: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Triky s typovým konstrukcemiSeznam typů

template< class H, class R>

struct List {

typedef H Head; typedef R Rest;

};

struct EmptyList {};

Funkce na seznamu typůtemplate< class L, int n> struct Nth {

typedef typename Nth< typename L::Rest, n-1>::Result Result;

};

template< class L> struct Nth< L, 0> {

typedef typename L::Head Result;

};

Page 328: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Triky s typovým konstrukcemiJiná implementace seznamu typů

template< class H, class R>

struct List;

struct EmptyList;

Funkce na seznamu typůtemplate< class L, int n> struct Nth;

template< class H, class R, int n> struct Nth< List< H, R>, n> {

typedef typename Nth< R, n-1>::Result Result;

};

template< class H, class R> struct Nth< List< H, R>, 0> {

typedef H Result;

};

Page 329: Objektov ě-orientované p rogramování v C++

Teoretický pohled na šablony

Výpočty při kompilaci Data:

celá čísla typy

Funkcionální programování: Funkce bez vedlejších efektů Neexistuje přiřazovací příkaz

• "Proměnné" se nemění

Rekurze Odlišného chování funkcí pro

různé hodnoty parametrů se dociluje definováním několika těl funkcí (tj. šablon)

Výpočty za běhu Data:

celá i reálná čísla, struktury ukazatelé

Procedurální programování: Procedury s vedlejšími efekty Destruktivní přiřazení

• Proměnné se mění

Podmínky, cykly, rekurze Odlišného chování procedur

pro různé hodnoty parametrů se dociluje podmínkami uvnitř

Page 330: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován polem

Slabě bezpečná implementace: Při výjimce v konstruktoru

proměnné top se nestane nic Při výjimce v new korektně

zanikne proměnná top a zůstane zachován původní stav

Funkce swap ani operator delete nezpůsobují výjimky

class StringStack {

public:

// ... 

private:

String * p_;

int n_;

};

String StringStack::pop()

{

if ( ! n_ )

return String();

String top = p_[ n_-1];

String * p2 = new String[ n_-1];

for ( int i = 0; i < n_-1; ++i)

swap( p2[ i], p_[ i]);

swap( p_, p2);

--n_;

delete p2;

return top;

}

Page 331: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován polem

Slabě bezpečná implementace Není silně bezpečná:

Funkce vrací hodnotou Pokud při vracení dojde k

výjimce v copy-constructoru, zásobník již bude zkrácen

class StringStack {

public:

// ... 

private:

String * p_;

int n_;

};

String StringStack::pop()

{

if ( ! n_ )

return String();

String top = p_[ n_-1];

String * p2 = new String[ n_-1];

for ( int i = 0; i < n_-1; ++i)

swap( p2[ i], p_[ i]);

swap( p_, p2);

--n_;

delete p2;

return top;

}

Page 332: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován polem

Slabě bezpečná implementace Není silně bezpečná:

Funkce vrací hodnotou Pokud při vracení dojde k

výjimce v copy-constructoru, zásobník již bude zkrácen

Tuto výjimku lze ošetřittry-blokem okolo příkazu return

• Uvést zásobník do původního stavu

• Ale: co když se uvedení do původního stavu nezdaří?

String StringStack::pop()

{

if ( ! n_ )

return String();

String top = p_[ n_-1];

String * p2 = new String[ n_-1];

for ( int i = 0; i < n_-1; ++i)

swap( p2[ i], p_[ i]);

swap( p_, p2);

--n_;

delete p2;

try {

return top;

}

catch ( ...)

{

push( top);

throw;

}

}

Page 333: Objektov ě-orientované p rogramování v C++

Exception-safe programming

Příklad: StringStack::pop Zásobník prvků typu String Implementován polem

Nefunkční implementace Není silně bezpečná:

Funkce vrací hodnotou Pokud při vracení dojde k

výjimce v copy-constructoru, zásobník již bude zkrácen

Tuto výjimku lze ošetřittry-blokem okolo příkazu return

Dokážeme udělat obnovení původního stavu bez nebezpečí výjimky

• Ale: jak zrušíme proměnnou p2, když k výjimce nedojde?

String StringStack::pop()

{

if ( ! n_ )

return String();

String top = p_[ n_-1];

String * p2 = new String[ n_-1];

for ( int i = 0; i < n_-1; ++i)

swap( p2[ i], p_[ i]);

swap( p_, p2);

--n_;

// tady bylo delete p2;

try {

return top;

}

catch ( ...)

{

for ( int i = 0; i < n_-1; ++i)

swap( p2[ i], p_[ i]);

++n_;

swap( p_, p2);

delete p2;

throw;

}

}

Page 334: Objektov ě-orientované p rogramování v C++

Oblasti platnosti identifikátorů

Oblasti platnosti identifikátorů Modul/namespace: Funkce, proměnné, typy a výčtové konstanty Třída: Metody, položky (příp. typy a výčtové konstanty) Funkce (blok): Parametry a lokální proměnné (příp. typy a výčtové

konstanty)

Pořadí vyhledávání identifikátoru Nejprve ve funkci: Od nejvnitřnějšího k hlavnímu bloku Poté v třídách (uvnitř metod): Od potomků k předkům Nakonec v modulu resp. aktuálním namespace

• S uplatněním direktiv using

Page 335: Objektov ě-orientované p rogramování v C++

Ochrana přístupu

Vztah vyhledávání identifikátoru a kontroly přístupu V definovaném pořadí se najde první oblast platnosti, ve které se

identifikátor vyskytuje• Namespace slité direktivami using se považují za jednu oblast platnosti

Jedná-li se o přetížený identifikátor funkce, vybere se v nalezené oblasti odpovídající varianta

• Neuplatní se tudíž jiné varianty v jiných oblastech, které by jinak byly viditelné

Aplikuje se mechanismus ochrany přístupových práv.• Pokud tedy nejlépe padnoucí varianta není přístupná, kompilátor ohlásí

chybu bez ohledu na případnou existenci jiné aplikovatelné a přístupné varianty

Page 336: Objektov ě-orientované p rogramování v C++

Preprocesor a překlad

Code...pushcall ...call...ret

Import_printf_getch

Export_main

Data‘H’,’e’,’l’,’l’,’o’,10,0

hello.o

hello.i/*...*/int printf( const char *, ...);/*...*/int getch();int putch();/*...*/

int main( int argc, char * * argv){ printf( “Hello\n”); getch(); return 0;}

stdio.h/*...*/int printf( const char *, ...);/*...*/

hello.c#include <stdio.h>#include <conio.h>

int main( int argc, char * * argv){ printf( “Hello\n”); getch(); return 0;}

conio.hint getch();int putch();/*...*/

Page 337: Objektov ě-orientované p rogramování v C++

Spojování modulů

Code...pushcall ...call...ret

Import_printf_getch

Export_main

Data‘H’,’e’,’l’,’l’,’o’,10,0

hello.o

Code...call ...call

Import_main_exit

Exportentry point

Data

cstart.o

Code...

Import

Export_printf

Data

printer.o

Code...

Import

Export_getch

Data

creader.o

Codesyscall ...

Import

Export_exit

Data

exit.o

Page 338: Objektov ě-orientované p rogramování v C++

Spustitelný program a spuštěný proces

...pushcall ...call...ret

‘H’,’e’,’l’,’l’,’o’,10,0

hello

...call ...call

entry point

...

...

syscall ...

Code

Data

Code

Data

00000000

Heap

Stack

Adresový prostorIP

R0R1...

FFFFFFFF

Page 339: Objektov ě-orientované p rogramování v C++

***** C++ *****

Identifikátory a kontexty Zapouzdření Dědičnost Přetěžování funkcí Předefinování operátorů

Objekty Konstruktory a destruktory

Pozdní vazba (late binding) Virtuální funkce

Neviditelné konverzeDrobná vylepšení

Striktnější typová kontrola oproti C

new a delete Komentáře

ŠablonyZpracování výjimekKnihovny

Streams

Page 340: Objektov ě-orientované p rogramování v C++

Virtuální funkce – Specializace datové strukturyclass HashTable {

public:

void add(

const void * key,

int keylen,

const void * data,

int datalen);

protected:

virtual long hash(

const void * key,

int keylen);

private:

SmartArray _tbl;

};

class StringTable

: public HashTable {

public:

void add(

const char * key,

const void * data,

int datalen);

protected:

virtual long hash(

const void * key,

int keylen);

};

Page 341: Objektov ě-orientované p rogramování v C++

Virtuální funkce – Užití datové strukturyclass Catalog

: private StringTable {

public:

void add(

const char * isbn,

const char * author,

const char * title);

private:

virtual long hash(

const void * key,

int keylen);

};

class Catalog {

public:

void add(

const char * isbn,

const char * author,

const char * title);

private:

StringTable _t;

};

Page 342: Objektov ě-orientované p rogramování v C++

Virtuální funkce - Řešení v C-stylu

Abstraktní třída

struct File {

int handle;

int (* readf)(

File * p);

void (* writef)(

File * p, int x);

};

int read( File * p)

{ return p->readf( p); }

  

Specializace 

int bread( File * p)

{ /* ... */ }

 

File * bopen()

{

File * p = new File;

p->handle = /*...*/;

p->readf = bread;

p->writef = bwrite;

return p;

}

  

Page 343: Objektov ě-orientované p rogramování v C++

Virtuální funkce - Řešení v C-stylu

Jiná specializace

struct TFile {

File f;

int info;

};

  

int tread( File * p)

{ TFile * tp = (TFile *)p;

/* ... */

}

File * topen()

{ TFile * tp = new TFile;

tp->f.handle = /* ... */;

tp->f.readf = tread;

tp->f.writef = twrite;

tp->info = /* ... */

return &tp->f;

}

 

Page 344: Objektov ě-orientované p rogramování v C++

Virtuální funkce - Řešení v C-stylu

Lepší implementace

struct VTFile {

int (* readf)( File * p);

void (* writef)(

File * p, int x);

};

struct File {

int handle;

const VTFile * vt;

};

const VTFile VTBFile =

{ bread, bwrite };

const VTFile VTTFile =

{ tread, twrite };

 

Page 345: Objektov ě-orientované p rogramování v C++

Virtuální funkce - Řešení v C++

Abstraktní třída

class File {

int handle;

public:

virtual int readf()

= 0;

virtual void writef(

int x) = 0;

};

int read( File * p)

{ return p->readf(); }

Specializace

class BFile : public File {

virtual int readf();

virtual void writef(

int x);

};

int BFile::readf()

{ /* ... */

}

File * bopen()

{ BFile * p = new BFile;

p->handle = /* ... */;

return p;

}

Page 346: Objektov ě-orientované p rogramování v C++

Virtuální funkce - Řešení v C++

Jiná specializace

class TFile : public File {

int info;

virtual int readf();

virtual void writef(

int x);

};

int TFile::readf()

{ /* ... */ }

File * topen()

{ TFile * p = new TFile;

p->handle = /* ... */;

p->info = /* ... */;

return p;

}

Page 347: Objektov ě-orientované p rogramování v C++

Statické a dynamické volání virtuálních metodvoid x()

{

A a; /* A::A */

B b; /* B::B */

A & raa = a;

A & rab = b;

B & rbb = b;

A * paa = &a;

A * pab = &b;

B * pbb = &b;

a.f(); /* A::f (c) */

b.f(); /* B::f (c) */

raa.f(); /* A::f (r) */

rab.f(); /* B::f (r) */

rbb.f(); /* B::f (r) */

paa->f(); /* A::f (r) */

pab->f(); /* B::f (r) */

pbb->f(); /* B::f (r) */

b.A::f(); /* A::f (c) */

b.B::f(); /* B::f (c) */

paa->A::f(); /* A::f (c) */

pab->A::f(); /* A::f (c) */

pbb->A::f(); /* A::f (c) */

pbb->B::f(); /* B::f (c) */

raa.A::f(); /* A::f (c) */

rab.A::f(); /* A::f (c) */

rbb.A::f(); /* A::f (c) */

rbb.B::f(); /* B::f (c) */

}

Page 348: Objektov ě-orientované p rogramování v C++

String č. 5 - operator [ ]

class String {

friend class StringPos;

public:

 

const char & operator[]( int pos)

const

{

/* test pos */

return _body->buffer()[ _pos];

};

 

StringPos operator[]( int pos)

{

/* test pos */

return StringPos( this, pos);

};

 

/* ... */

};

Page 349: Objektov ě-orientované p rogramování v C++

String č. 5 - operator [ ]

class StringPos {

public:

StringPos( String * ref, int pos)

{

_ref = ref;

_pos = pos;

};

operator char() const

{

return _ref->_body->buffer()[ _pos];

};

char operator=( char x) const

{

_ref->make_private();

_ref->_body->buffer()[ _pos] = x;

return x;

}

private:

String * _ref;

int _pos;

};

Page 350: Objektov ě-orientované p rogramování v C++

String č. 6 - Držadlo

class StringBody;

class String {

public:

String();

String( const String &);

const String & operator=( const String &);

~String();

 

String( const char *);

const char * c_str() const;

 

String cat( const String &) const;

String cat( const char *) const;

String catr( const char *) const;

 

private:

String( StringBody *);

 

StringBody * _body;

};

Page 351: Objektov ě-orientované p rogramování v C++

String č. 6 - Abstraktní tělo

class StringBody {

public: 

virtual ~StringBody() {}

void inc() { _count++; };

void dec();

virtual bool private() { return _count == 1; }

virtual StringBody * freeze() = 0;

virtual StringBody * make_private() = 0;

virtual int length() = 0;

virtual void copy( char * dst) = 0;

virtual const char * c_str() = 0;

virtual char read_at( int i) = 0;

virtual void write_at( int i, char ch) = 0;

protected:

StringBody() { _count = 0; }

private:

int _count;

};

Page 352: Objektov ě-orientované p rogramování v C++

String č. 6 - Jednoduché tělo

class StringBodySimple : public StringBody {

public:

static StringBodySimple * create( const char * s);

protected:

StringBodySimple( int n);

virtual ~StringBodySimple() { delete _str; }

virtual StringBody * freeze() { return this; }

virtual StringBody * make_private();

virtual int length() { return _len; }

virtual void copy( char * dst) { memcpy( dst, _str, _len); }

virtual const char * c_str() { return _str; }

virtual char read_at( int i)

{ /* test! */ return _str[ i]; }

virtual void write_at( int i, char ch)

{ /* test! */ _str[ i] = ch; }

private:

char * _str;

int _len;

};

Page 353: Objektov ě-orientované p rogramování v C++

String č. 6 - Jednoduché tělo

StringBodySimple * StringBodySimple::create( const char * s)

{

StringBodySimple * p = new StringBodySimple( strlen( s));

strcpy( p->_str, s);

return p;

}

StringBodySimple::StringBodySimple( int n)

{ _str = new char[ n + 1]; _len = n;

}

StringBody * StringBodySimple::make_private()

{ if ( private() )

return this;

StringBodySimple * p = new StringBodySimple( _len);

strcpy( p->_str, _str);

return p;

}

Page 354: Objektov ě-orientované p rogramování v C++

String č. 6 - Složené tělo

class StringBodyConcatenate : public StringBody {

public:

static StringBodyConcatenate * create(

StringBody * a, StringBody * b);

protected:

StringBodyConcatenate( StringBody * a, StringBody * b);

virtual ~StringBodyConcatenate();

virtual StringBody * freeze();

virtual StringBody * make_private();

virtual int length()

{ return _a->length() + _b->length(); }

virtual void copy( char * dst);

virtual const char * c_str()

{ /* error */ return 0; }

virtual char read_at( int i);

virtual void write_at( int i, char ch);

private:

StringBody * _a, * _b;

};

Page 355: Objektov ě-orientované p rogramování v C++

String č. 6 - Složené tělo

StringBodyConcatenate * StringBodyConcatenate::create(

StringBody * a, StringBody * b)

{ return new StringBodyConcatenate( a, b);

}

StringBodyConcatenate::StringBodyConcatenate(

StringBody * a, StringBody * b)

{ _a = a; _a->inc();

_b = b; _b->inc();

}

StringBodyConcatenate::~StringBodyConcatenate()

{ _a->dec();

_b->dec();

}

Page 356: Objektov ě-orientované p rogramování v C++

String č. 6 - Složené tělo

StringBody * StringBodyConcatenate::freeze()

{ StringBodySimple * p = new StringBodySimple( length());

copy( p->_str);

return p;

}

StringBody * StringBodyConcatenate::make_private()

{ if ( private() )

return this;

return new StringBodyConcatenate(

_a->make_private(), _b->make_private());

}

Page 357: Objektov ě-orientované p rogramování v C++

String č. 6 - Složené tělo

int StringBodyConcatenate::length()

{ return _a->length() + _b->length();

}

void StringBodyConcatenate::copy( char * dst)

{ _a->copy( dst);

_b->copy( dst + _a->length());

}

char StringBodyConcatenate::read_at( int i)

{ return i < _a->length()

? _a->read_at( i)

: _b->read_at( i - _a->length());

}

void StringBodyConcatenate::write_at( int i, char ch)

{ i < _a->length()

? _a->write_at( i, ch)

: _b->write_at( i - _a->length(), ch);

}

Page 358: Objektov ě-orientované p rogramování v C++

Problém

Problém: Vzájemné volání dec() a destruktoru je rekurzivní

void StringBody::dec()

{

if ( ! --count )

delete this;

};

StringBodyConcatenate::~StringBodyConcatenate()

{ _a->dec();

_b->dec();

}

Hloubka vnoření rekurze může být neúnosně velká{ String x;

for ( int i=0; i < 1000000; i++ )

x = x + "a";

}

Page 359: Objektov ě-orientované p rogramování v C++

Řešení 1

Nahradit rekurzi pomocnou strukturou a cyklemtypedef std::stack< StringBody *> KillStack;

inline void StringBody::dec()

{

if ( ! --count )

killMe();

}

void StringBody::killMe()

{ KillStack toBeKilled;

StringBody * b = this;

for (;;)

{

b->prepareToDie( toBeKilled);

delete b;

if ( toBeKilled.empty() )

break;

b = toBeKilled.top();

toBeKilled.pop();

}

}

Page 360: Objektov ě-orientované p rogramování v C++

Řešení 1

StringBodyConcatenate::prepareToDie( KillStack & tbk)

{ _a->dec( tbk);

_a = 0;

_b->dec( tbk);

_b = 0;

}

void StringBody::dec( KillStack & tbk)

{

if ( ! --count )

{

tbk.push( this);

}

};

Page 361: Objektov ě-orientované p rogramování v C++

Řešení 1

Pomocná strukturatypedef std::stack< StringBody *> KillStack;

Nevýhody: Časová náročnost operací struktury není zanedbatelná

• K rozsáhlému mazání může dojít v rámci téměř každé operace

Práce se strukturou vyžaduje alokaci• Deadlock: Ke zrušení dat je třeba naalokovat další

• Exception-safety: Destruktor nemá vyvolávat výjimky

Page 362: Objektov ě-orientované p rogramování v C++

Řešení 2

Jiné řešení V okamžiku zrušení držadla String lze určit množinu uzlů typu

StringBodyConcatenate, které jsou dosažitelné pouze z tohoto držadla

Tato množina je binární strom

Ekvivalentní úloha Zrušit binární strom bez rekurze a dalších datových struktur

• Pokud možno v lineárním čase

Tato úloha je řešitelná postupným přeskupováním stromu a umazáváním uzlů s méně než dvěma syny

Stále přetrvává problém s občasnými výskyty časově náročných operací

• Nevhodné pro real-time operace apod.

Page 363: Objektov ě-orientované p rogramování v C++

Problém

Problém: Implementace mnoha funkcí je rekurzivní

void StringBodyConcatenate::copy( char * dst) const

{ _a->copy( dst);

_b->copy( dst + _a->length());

}

Page 364: Objektov ě-orientované p rogramování v C++

Řešení ?

Pokus o náhradu rekurze opakováním

Funkce copySomething okopíruje tolik, kolik dokáže

class StringBody {

/* ... */

virtual int copySomething( char * dst, int offset) const = 0;

};

Funkce copy ji volá tak dlouho, dokud něco vrací

void String::copy( char * dst) const

{ int offset, len;

offset = 0;

while ( (len = _body->copySomething( dst + offset, offset)) > 0 )

offset += len;

}

Page 365: Objektov ě-orientované p rogramování v C++

Řešení ?

Problém: Implementace funkce copySomething je stále rekurzivní

int StringBodyConcatenate::copySomething(

char * dst, int offset) const

{

if ( offset < _a->length() )

return _a->copySomething( dst, offset);

else

return _b->copySomething(

dst + _a->length(), offset - _a->length());

}

Page 366: Objektov ě-orientované p rogramování v C++

Řešení ?

Tail-recursion Rekurzivní volání na konci funkce

void F( T p)

{

H( p);

if ( C( p) )

F( G( p));

}

Obecně lze nahradit cyklem

void F( T p)

{

H( p);

while ( C( p) )

{

p = G( p);

H( p);

}

}

Page 367: Objektov ě-orientované p rogramování v C++

Řešení ?

Tail-recursion Problém: Funkce copySomething je virtuální Řešení: Pomocná funkce redirect

• Vrací odkaz na uzel, který je zodpovědný za danou poziciclass StringBody {

/* ... */

virtual const StringBody * redirect( int offset) const

{

return 0; // no redirection

}

};

const StringBody * StringBodyConcatenate::redirect( int offset) const

{

if ( offset < _a->length() )

return _a;

else

return _b;

}

Page 368: Objektov ě-orientované p rogramování v C++

Řešení ?

Tail-recursion Problém: Funkce copySomething je virtuální Řešení: Pomocná funkce redirect

void String::copy( char * dst) const

{ int offset, len;

const String * p, * p2;

offset = 0;

do {

p = _body;

while ( !! (p2 = p->redirect( offset)) )

p = p2;

len = p->copySomething( dst + offset, offset);

offset += len;

} while ( len > 0 );

}

Page 369: Objektov ě-orientované p rogramování v C++

Řešení ?

Pyrrhovo vítězství: Rekurze je odstraněna Algoritmus má nyní kvadratickou složitost

Page 370: Objektov ě-orientované p rogramování v C++

Závěr

Srovnání možností Zrušení stromu lze provést bez rekurze a pomocných struktur v

lineárním čase Průchod stromem, který nemá být zničen, takto udělat nelze

• Ledaže by ve stromě byly zpětné odkazy

• Naše struktura ovšem není strom, ale DAG

Srovnání požadavků Rušení stromu musí být bezpečná, vždy proveditelná akce

• Protože se volá z destruktorů

Běžné operace na stromě takto bezpečné být nemusejí• Mohou tedy využívat pomocné struktury

• Je nutné definovat způsob indikace chyby

Page 371: Objektov ě-orientované p rogramování v C++

STL – vector

template<class T, class A = allocator<T> > class vector {

public:

typedef A allocator_type;

typedef A::size_type size_type;

typedef A::difference_type difference_type;

typedef A::reference reference;

typedef A::const_reference const_reference;

typedef A::value_type value_type;

typedef /*...*/ iterator;

typedef /*...*/ const_iterator;

typedef /*...*/ reverse_iterator;

typedef /*...*/ const_reverse_iterator;

explicit vector(const A& al = A());

explicit vector(size_type n, const T& v = T(), const A& al = A());

vector(const vector& x);

vector(const_iterator first, const_iterator last, const A& al = A());

/* ... */

Page 372: Objektov ě-orientované p rogramování v C++

STL – vector

/* template<class T, class A = allocator<T> > class vector { ... */

iterator begin();

const_iterator begin() const;

iterator end();

iterator end() const;

reverse_iterator rbegin();

const_reverse_iterator rbegin() const;

reverse_iterator rend();

const_reverse_iterator rend() const;

size_type size() const;

bool empty() const;

reference at(size_type pos);

const_reference at(size_type pos) const;

reference operator[](size_type pos);

const_reference operator[](size_type pos) const;

/* ... */

Page 373: Objektov ě-orientované p rogramování v C++

STL – vector

/* template<class T, class A = allocator<T> > class vector { ... */

reference front();

const_reference front() const;

reference back();

const_reference back() const;

void push_back(const T& x);

void pop_back();

void assign(const_iterator first, const_iterator last);

void assign(size_type n, const T& x = T());

iterator insert(iterator it, const T& x = T());

void insert(iterator it, size_type n, const T& x);

void insert(iterator it, const_iterator first, const_iterator last);

iterator erase(iterator it);

iterator erase(iterator first, iterator last);

void clear();

void swap(vector x);

/* ... */

Page 374: Objektov ě-orientované p rogramování v C++

STL – vector

/* template<class T, class A = allocator<T> > class vector { ... */

protected:

A allocator;

};

Page 375: Objektov ě-orientované p rogramování v C++

STL – vector – naivní implementace – hlavička

template<class T, int delta> class oriented_iterator;

template<class T, int delta> class const_oriented_iterator;

template<class T> class vector {

public:

typedef unsigned int size_type;

typedef int difference_type;

typedef T & reference;

typedef const T & const_reference;

typedef T value_type;

typedef oriented_iterator< T, 1> iterator;

typedef const_oriented_iterator< T, 1> const_iterator;

typedef oriented_iterator< T, -1> reverse_iterator;

typedef const_oriented_iterator< T, -1> const_reverse_iterator;

/* ... */

private:

size_type _n;

T * _p;

};

Page 376: Objektov ě-orientované p rogramování v C++

STL – vector – naivní implementace – přístup

template< class T> reference vector< T>::at(size_type pos)

{ return pos >= 0 && pos < n ? _p[ pos] : _dummy(); }

template< class T> const_reference vector< T>::at(size_type pos) const

{ return pos >= 0 && pos < n ? _p[ pos] : _dummy(); }

template< class T> reference vector< T>::operator[](size_type pos)

{ return at( pos); }

template< class T> const_reference vector< T>::operator[](size_type pos) const

{ return at( pos); }

template< class T> reference vector< T>::dummy() const

{ return *(T*)0; }

Page 377: Objektov ě-orientované p rogramování v C++

STL – vector – naivní implementace - konstrukce

template< class T> vector< T>::vector()

{ _n = 0; _p = 0; }

template< class T> vector< T>::vector( size_type n, const T & v)

{

_n = n;

_p = new T[ _n]; /* nevolat konstruktory !!! */

for ( int i = 0; i < _n; i++)

{ /* zavolat konstruktory !!! */

/* _p[ i].T( v); */

}

}

template< class T> vector< T>::vector( const vector & x)

{

_n = x._n;

_p = new T[ _n]; /* nevolat konstruktory !!! */

for ( int i = 0; i < _n; i++)

{ /* zavolat konstruktory !!! */

/* _p[ i].T( x._p[ i]); */

}

}

Page 378: Objektov ě-orientované p rogramování v C++

STL – vector – naivní implementace – push/pop

template< class T> void vector< T>::push_back(const T& x)

{

T * p = _p;

_n = _n + 1;

_p = new T[ _n + 1]; /* nevolat konstruktory !!! */

for ( int i = 0; i < _n - 1; i++)

{ /* zavolat konstruktory !!! */

/* _p[ i].T( p[ i]); */

}

}

template< class T> void vector< T>::pop_back()

{

/* ... */

}

Page 379: Objektov ě-orientované p rogramování v C++

STL – vector – vylepšená implementace - konstrukce

void * operator new( size_t s, void * p)

{ return p; }

template< class T> vector< T>::vector( size_type n, const T & v)

{

_n = n;

_p = reinterpret_cast< T *>( new char[ _n * sizeof( T)]);

for ( int i = 0; i < _n; i++)

{

new( _p[ i]) T( v);

}

}

template< class T> vector< T>::~vector()

{

for ( int i = 0; i < _n; i++)

{

_p[ i]->T::~T();

}

delete[] reinterpret_cast< char *>( _p);

}

Page 380: Objektov ě-orientované p rogramování v C++

STL – vector – naivní implementace – iterátory

template< class T, int delta> class oriented_iterator {

public:

T & operator *() const

{ return _i < _v->_n ? *( T *)0 : _v->_p[ _i];

}

const oriented_iterator< T, delta> & operator ++() /* prefix */

{ if ( _i < _v->_n ) _i++;

return * this;

}

oriented_iterator< T, delta> operator ++( int) /* postfix */

{ oriented_iterator< T, delta> old = * this;

operator ++(); return old;

}

bool operator ==( const oriented_iterator< T, delta> & b) const

{ return _i == _b->_i; }

private:

friend class vector< T>;

oriented_iterator( vector< T> * v, int i) { _v = v; _i = i; }

vector< T> * _v;

int _i;

};

Page 381: Objektov ě-orientované p rogramování v C++

STL – vector – naivní implementace – iterátory

template< class T> iterator vector< T>::begin()

{ return iterator( this, 0); }

template< class T> const_iterator vector< T>::begin() const

{ return const_iterator( this, 0); }

template< class T> iterator vector< T>::end()

{ return iterator( this, _n); }

template< class T> iterator vector< T>::end() const

{ return const_iterator( this, _n); }

Page 382: Objektov ě-orientované p rogramování v C++

Operátory new a delete

Operátory new a deletemohou být definovány jako globální nebo uvnitř třídy

na rozdíl od ostatních operátorů jsou i uvnitř třídy považovány za statické funkce

Operátory deklarované uvnitř třídy jsou použity při alokaci resp. dealokaci objektů této třídy (a jejích potomků)

Globální operátory jsou používány pro třídy bez těchto operátorů a pro datové typy, které nejsou třídami, včetně polí tříd

void * operator new( size_t s);

void operator delete( void * p);

Page 383: Objektov ě-orientované p rogramování v C++

Operátory new a delete

Operátor new může mít přídavné parametry a může tak být přetížen

Typické použití void * operator new( size_t s, void * p){ return p;

}

void call_constructor( X * p, int param){ new( p) X( param);

}

Vztah operátoru new a konstruktoruX * p = new X(a,b,c);

(nekorektní) ekvivalentX * p = (X*)X::operator new(sizeof(X));

if (p) p->X::X(a,b,c); // zakázáno

Vztah operátoru delete a destruktorudelete p;

ekvivalentif (p) p->X::~X(); // povoleno

X::operator delete((void*)p);

Page 384: Objektov ě-orientované p rogramování v C++

Šablony tříd – explicitní instanciace

Je-li předem známa množina typů (formálních parametrů), pro něž se bude šablona instanciovat, není nutno publikovat těla metod ve zdrojové formě a je možné je předkompilovat do knihovny

Veřejný hlavičkový soubor X.h – hlavička třídytemplate< class T> class X

{ /* ... */

void f(/*...*/);

};

Nepublikovaný hlavičkový soubor XBody.h Generická těla metod

#include "X.h"

template< class T> void X< T>::f(/*...*/)

{ /*...*/ }

Knihovní modul XBodyInt.cpp Instanciace pro typ int

#include "XBody.h"

template X< int>;

Page 385: Objektov ě-orientované p rogramování v C++