PRG032 2007

Views:
 
Category: Entertainment
     
 

Presentation Description

No description available.

Comments

Presentation Transcript

Objektově-orientované programování v C++: 

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

Pravidla studia: 

Pravidla studia PRG032 2/2 Z,Zk

Zápis na cvičení: 

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 nebudou automaticky zapsáni Zapsáni musejí být všichni Do 12.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 12.10. zapsán, zápočet v tomto šk. roce nedostane

Udělení zápočtu: 

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.2008 Doladění do konce výuky v letním semestru

Zkouška: 

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

Pravidla pro budoucí neúspěšné: 

Pravidla pro budoucí neúspěšné Tento předmět v příštím školním roce Bude formálně vypsán Nebude probíhat výuka Bude otevřen Grupík stejně jako letos Budou zkušební termíny Zkouška Pokud letos složíte zkoušku se známkou výborně nebo velmi dobře a nedostanete zápočet, bude vám příští rok uznána Tento mechanismus je implementován zkoušejícími, nikoliv studijním oddělěním 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 v Grupíku k některému z cvičících a dohodnout se s ním na konkrétních podmínkách

Obsah: 

Obsah

Obsah cvičení: 

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í

Obsah přednášky: 

Obsah přednášky

Literatura: 

Literatura

Literatura: 

Literatura Pro začátečníky Bruce 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)

Literatura: 

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)

Literatura: 

Literatura Až si budete myslet, že všechno umíte Andrei Alexandrescu: Modern C++ Design (2001) Moderní programování v C++ (Computer Press 2004) David Vandevoorde, Nicolai M. Josuttis: C++ Templates (2003)

Normy: 

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 (2009?) 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)

www: 

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

C++: 

C++

Třída a objekt: 

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řídy Reprezentace 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

Třída a objekt: 

Třída a objekt Instanciace třídy = vznik objektu tohoto typu Jako 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

Objekt a ukazatel na objekt: 

Objekt a ukazatel na objekt C++ důsledně odlišuje objekt a ukazatel na něj Triviální důsledek: class T { /*...*/ }; T * p; // zde nevzniká objekt typu T

Objekt a ukazatel na objekt: 

Objekt a ukazatel na objekt C++ důsledně odlišuje objekt a ukazatel na něj Triviá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 typu class U : public T { /*...*/ }; U y; T x = y; // toto je kopie části objektu y do vznikajícího objektu x

Objekt a ukazatel na objekt: 

Objekt a ukazatel na objekt C++ důsledně odlišuje objekt a ukazatel na něj Triviá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 typu class 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-constructoru T::T( const T &); a díky možnosti konvertovat odkaz na potomka na odkaz na předka: U => U & => T & => const T &

Objekt a ukazatel na objekt: 

Objekt a ukazatel na objekt C++ důsledně odlišuje objekt a ukazatel na něj Triviá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 typu class 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

Objekt a ukazatel na objekt: 

Objekt a ukazatel na objekt C++ důsledně odlišuje objekt a ukazatel na něj Triviá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 typu class 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, ...)

Objekt a ukazatel na objekt: 

Objekt a ukazatel na objekt C++ důsledně odlišuje objekt a ukazatel na něj Nefunguje 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

Objekt a ukazatel na objekt: 

Objekt a ukazatel na objekt C++ důsledně odlišuje objekt a ukazatel na něj Nefunguje 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

Objekt a ukazatel na objekt: 

Objekt a ukazatel na objekt C++ důsledně odlišuje objekt a ukazatel na něj Nefunguje 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

Objekt a ukazatel na objekt: 

Objekt a ukazatel na objekt C++ důsledně odlišuje objekt a ukazatel na něj Nefunguje 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ů

Objekt a ukazatel na objekt: 

Objekt a ukazatel na objekt Funkce jako add nemůže vracet referenci add vrací hodnotu různou od všech svých parametrů hodnotu parametrů nesmí měnit reference 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

Objekt a ukazatel na objekt: 

Objekt a ukazatel na objekt Funkce jako add nemůže vracet referenci add vrací hodnotu různou od všech svých parametrů hodnotu parametrů nesmí měnit reference 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 ?

Objekt a ukazatel na objekt: 

Objekt a ukazatel na objekt Funkce jako add nemůže vracet referenci add vrací hodnotu různou od všech svých parametrů hodnotu parametrů nesmí měnit reference 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));

Třídy 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

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

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

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

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"

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

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 &

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

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 &

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

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"

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

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

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

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 &

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

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

Třídy v C++: 

Třídy v C++ Třídy sloužící jako datové typy Promě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é“ objekty Proměnné typu T *, případně T & Objekty alokovány dynamicky Kopírování nemívá smysl Metody měnící stav objektu Většinou s dědičností a virtuálními funkcemi

Třídy v C++: 

Třídy v C++ Abstraktní třída Definice v C++: Třída obsahující alespoň jednu čistě virtuální funkci Běžná definice: Třída, která sama nebude instanciována Představuje rozhraní, které mají z ní odvozené třídy (potomci) implementovat Konkrétní třída Třída, určená k samostatné instanciaci Implementuje rozhraní, předepsané abstraktní třídou, ze které je odvozena

Dědičnost a destruktor : 

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

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

Ideální užití dědičnosti a virtuálních funkcí Abstraktní třída Definuje 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 };

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

Ideální užití dědičnosti a virtuálních funkcí Abstraktní třída Definuje 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 };

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

Ideální užití dědičnosti a virtuálních funkcí Abstraktní třída Definuje rozhraní objektu jako množinu předepsaných virtuálních funkcí Konkrétní třída Implementuje předepsané virtuální funkce Je potomkem abstraktní třídy Dědičnost jako vztah rozhraní-implementace

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

Ideální užití dědičnosti a virtuálních funkcí Abstraktní třída Definuje 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_; };

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

Ideální užití dědičnosti a virtuálních funkcí Abstraktní třída Definuje rozhraní objektu jako množinu předepsaných virtuálních funkcí Konkrétní třída Implementuje předepsané virtuální funkce Polotovar 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

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

Ideální užití dědičnosti a virtuálních funkcí Polotovar třídy Mezi 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_; };

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

Ideální užití dědičnosti a virtuálních funkcí Konkrétní třída Implementuje předepsané virtuální funkce Konkré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_; };

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

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éž mechanismy Některé jazyky tyto účely rozlišují (Java)

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

Ideální užití dědičnosti a virtuálních funkcí Ideální abstraktní třída Pouze č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

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

Ideální užití dědičnosti a virtuálních funkcí Ideální abstraktní třída Pouze č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ář

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

Ideální užití dědičnosti a virtuálních funkcí Ideální abstraktní třída Pouze č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čnost Obvykle musí být virtuální, aby odpovídala sjednocení: Fyzikář = Pedagogika + Fyzika Matikář = Pedagogika + Matematika Fyzikář+Matikář nemá mít dvě rozhraní pro Pedagogiku

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

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

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

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

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

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(); }

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

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(); }

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

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 implementaci class GraphicObject { /* ... čistě virtuální funkce ... */ private: friend class Scene; Item * next_; }; class Scene { public: void insert( GraphicObject * go); void paintAll() const; private: GraphicObject * first_; };

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

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

Třída jako datový typ: 

Třída jako datový typ

Speciální metody tříd : 

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

Speciální metody tříd : 

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();

Speciální metody tříd : 

Speciální metody tříd Konverzní konstruktory class 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átory class 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

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

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

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

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í

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

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í.

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

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 &) Metodou A 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

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

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 &) Metodou A B::operator xxx() A B::operator xxx() const

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

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) Metodou A B::operator xxx( int) A B::operator xxx( int) const

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

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

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

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.

Complex: 

Complex Komplexní číslo

Complex: 

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()); } 

Poučení - konstruktory: 

Poučení - konstruktory Ve třídě Complex nejsou odkazy na data uložená jinde Vyhovuje 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í

Poučení - konstruktory: 

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

Poučení - konstruktory: 

Poučení - konstruktory Speciální konstruktor Complex( 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 fungovalo Complex operator+( const Complex & a, const Complex & b)

Poučení – konverzní operátor: 

Poučení – konverzní operátor Konverzní operátor operator 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á operace double 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ů

Poučení – konverzní operátor: 

Poučení – konverzní operátor Konverzní operátor operator 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> ...

Poučení - vracení hodnotou: 

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í - vracení hodnotou: 

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í hodnotou Vrací novou hodnotu, která jinde neexistuje return Complex( ...);

Poučení - vracení hodnotou: 

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í hodnotou Vrací novou hodnotu, která jinde neexistuje return Complex( ...); Ale: operator+= může vracet odkazem Vrací hodnotu levého operandu return * this;

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

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

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

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

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

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ů

String: 

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

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

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

Poučení - Konverzní operátor: 

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

String: 

String Ukázková implementace č. 2 (naivní, neefektivní)

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

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

Poučení - konstruktory: 

Poučení - konstruktory Ve třídě String jsou odkazy na data uložená jinde Chová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í

Poučení - konstruktory: 

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

Poučení - konstruktory: 

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(); }

Poučení - konstruktory: 

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...

String: 

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

String č. 3 - Counted-pointers: 

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

String č. 3 - Counted-pointers: 

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

String č. 3 - Counted-pointers: 

String č. 3 - Counted-pointers Kopie a destrukce String::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(); }

String č. 3 - Counted-pointers: 

String č. 3 - Counted-pointers Destrukce těla void StringBody::dec() { if ( ! --count ) delete this; }; StringBody::~StringBody() { delete[] _str; }  

String č. 3 - Counted-pointers: 

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

String č. 3 - Counted-pointers: 

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(); }  

String: 

String operator [ ]

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

String č. 4 - operator [ ] - čtení class String { public:   char operator[]( int pos) const { if ( pos < 0 || pos >= _body->length() ) return 0; return _body->buffer()[ _pos]; }; /* ... */ };

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

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;

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

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;

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

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(); } };

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

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 č. 4 - operator [ ] - čtení a zápis: 

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

String č. 4 - operator [ ] – rozlišení čtení a zápisu: 

String č. 4 - operator [ ] – rozlišení čtení a zápisu class 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_; };

Šablony: 

Šablony Templates

Šablony tříd - příklad: 

Šablony tříd - příklad Definice template< 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;

Šablony tříd - definice: 

Š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 šablony template< formální-parametry> lze použít před několika formami deklarací; oblastí platnosti formálních parametrů je celá prefixovaná deklarace

Šablony tříd - instanciace: 

Š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

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

Š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 variantu template< class T> class X { X< T>( const X< T> &); };

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

Š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.

Šablony tříd – triky: 

Š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 { /* ... */ };

Šablony funkcí: 

Š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

Šablony funkcí: 

Š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

Šablony - triky: 

Šablony - triky Parciální specializace Deklarovanou šablonu lze pro určité kombinace parametrů předefinovat jinak, než určuje její základní definice template< int n> class Array< n, bool> { /* specializace pro pole typu bool */ }; Krajním případem parciální specializace je explicitní specializace Explicitní 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...

Standardní knihovny C++: 

Standardní knihovny C++

namespace: 

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;

Standardní knihovny 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části Základní knihovny převzaté z C, podle nové konvence v přejmenovaných souborech Rozšířené C++ knihovny iostream: Systém znakového a formátovaného vstupu a výstupu STL: Standard Template Library

Základní knihovny C a 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ů

Rozšířené knihovny pro 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í)

STL: 

STL Standard Template Library

STL: 

STL Kontejnery Prefabriká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

STL – Příklad: 

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

STL – Kontejnery: 

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

STL – Kontejnery: 

STL – Kontejnery Pomocné metody kontejneru Test prázdnosti bool empty() const Počet prvků size_t size() const nepoužívat pro testy prázdnosti

STL – Kontejnery: 

STL – Kontejnery Metody kontejneru, vracející iterátory Odkaz na začátek kontejneru - první platný prvek iterator begin() const_iterator begin() const Odkaz za konec kontejneru - za poslední platný prvek iterator 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

STL – Kontejnery: 

STL – Kontejnery Operátory definované na iterátorech pří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)

STL – Kontejnery: 

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

STL – Kontejnery: 

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()); }

STL – Kontejnery: 

STL – Kontejnery Odvozené funkce kontejneru pro přístup k prvkům Prvky 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(); }

STL – Kontejnery: 

STL – Kontejnery Odvozené funkce kontejneru pro přístup k prvkům Prvky 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); }

STL – Kontejnery: 

STL – Kontejnery Odvozené funkce manipulace s konci kontejneru Př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 prvku void pop_front() { return erase( begin()); } list, deque void pop_back() { return erase( --end()); } list, deque, vector

STL - Kontejnery: 

STL - Kontejnery

STL - Kontejnery: 

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

STL - Kontejnery: 

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í

STL - Kontejnery: 

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

STL - Kontejnery: 

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

STL - Kontejnery: 

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

STL - Kontejnery: 

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žitost po operaci budou dříve existující iterátory neplatné

STL - Kontejnery: 

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žitost po operaci budou dříve existující iterátory neplatné

STL - Kontejnery: 

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é

STL - Kontejnery: 

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é

STL - Kontejnery: 

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é

STL - Kontejnery: 

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é

STL – Ostatní: 

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,...)

STL – Algoritmy: 

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

STL – Algoritmy: 

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

STL – Algoritmy: 

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

STL – Algoritmy: 

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

STL – Algoritmy: 

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

STL – Algoritmy: 

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

STL – Algoritmy: 

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

STL – Algoritmy: 

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

STL – Algoritmy: 

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

STL – Algoritmy: 

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

STL – Algoritmy: 

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ů

STL – Algoritmy: 

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

STL – Algoritmy: 

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

STL – Algoritmy: 

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

iostream: 

iostream vstupní a výstupní proudy

iostream: 

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

stringstream: 

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 *

iostream: 

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)

iostream: 

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

iostream: 

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

iostream: 

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

iostream: 

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

iostream: 

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

iostream: 

iostream Médium Konkrétní médium je implementováno jako potomek 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

sstream: 

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

fstream: 

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, close Operační systém

iostream: 

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)

iostream: 

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ápis basic_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;

iostream: 

iostream formátované čtení/zápis basic_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

iostream: 

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;

iostream: 

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

iostream: 

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

Koenig lookup: 

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

Koenig lookup: 

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

Koenig lookup: 

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)

iostream: 

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

iostream: 

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

iostream: 

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 &

iostream: 

iostream Iterátory nad proudy - příklady naplně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));

Exception handling: 

Exception handling Mechanismus výjimek

Exception handling: 

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ě zaniknou volá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; }

Exception handling: 

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ě zaniknou volá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; }

Exception handling: 

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ě zaniknou volá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; } }

Exception handling: 

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ě zaniknou volá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 ) { /*...*/ } }

Exception handling: 

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ě zaniknou volá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 ) { /*...*/ } }

Exception handling: 

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ě zaniknou volá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 &) { /*...*/ } }

Exception handling: 

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ě zaniknou volá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 (...) { /*...*/ } }

Exception handling: 

Exception handling Fáze zpracování výjimky Vyhodnocení 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

Exception handling: 

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ě

Exception handling: 

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í RTTI Odvozené 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

Exception handling: 

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í RTTI Odvozené 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

Exception handling: 

Exception handling Typické použití Deklarace výjimek class AnyException { /*...*/ }; class WrongException : public AnyException { /*...*/ }; class BadException : public AnyException { /*...*/ }; Vyvolání výjimek void 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"; } }

Exception handling: 

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ýjimek void 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(); } }

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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čí

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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é

Exception-safe programming: 

Exception-safe programming Kompilátory samy ošetřují některé výjimky Dynamická 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

Exception-safe programming: 

Exception-safe programming Kompilátory samy ošetřují některé výjimky Dynamická 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;

Exception-safe programming: 

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

Exception-safe programming: 

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"

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

Exception-safe programming Příklad: String č. 2 operator= Silně bezpečné řešení Pozorování: Toto řešení je "shodou okolností" imunní proti this == & 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; }

Exception-safe programming: 

Exception-safe programming Příklad: String č. 2 operator= Silně bezpečné řešení Pozorování: Toto řešení je "shodou okolností" imunní proti this == & 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; }

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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(); } }

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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řit try-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; } }

Exception-safe programming: 

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řit try-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; } }

Exception-safe programming: 

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é

Exception-safe programming: 

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 (...) { /* ??? */ }

Exception-safe programming: 

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 */ }

Exception-safe programming: 

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 */ }

Exception-safe programming: 

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

Exception specifications: 

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 */ }

Exception specifications: 

Exception specifications Exception specifications Kompilá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čí

Exception specifications: 

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í */ }

Exception specifications: 

Exception specifications Exception specifications Kompilá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

Cast: 

Cast Různé druhy přetypování

Přetypování: 

Přetypování Různé varianty syntaxe C-style cast (T)e Převzato z C

Přetypování: 

Přetypování Různé varianty syntaxe C-style cast (T)e Převzato z C Function-style cast T(e) Ekvivalentní (T)e T musí být syntakticky identifikátor nebo klíčové slovo označující typ

Přetypování: 

Přetypování Různé varianty syntaxe C-style cast (T)e Převzato z C Function-style cast T(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)

Přetypování: 

Přetypování Různé varianty syntaxe C-style cast (T)e Převzato z C Function-style cast T(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)

Přetypování: 

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í objekt class 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

Přetypování: 

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í objekt class Data { public: void register_pointer() const { references++; } private: /* ... data ... */ mutable int references; };

Přetypování: 

Přetypování static_cast<T>(e) Umožňuje Vš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

Přetypování: 

Přetypování static_cast<T>(e) Umožňuje Vš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)); } };

Přetypování: 

Přetypování static_cast<T>(e) Umožňuje Všechny implicitní konverze Konverze 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

Přetypování: 

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

Přetypování: 

Přetypování reinterpret_cast<T>(e) Umožňuje Konverze ukazatele na dostatečně velké celé číslo Konverze celého čísla na ukazatel Konverze mezi různými ukazateli na funkce Konverze 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ý

Přetypování: 

Přetypování dynamic_cast<T>(e) Umožňuje Konverze 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

Přetypování: 

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 ) { /* ... */ }

RTTI: 

RTTI Typová informace za běhu

RTTI: 

RTTI Operátor typeid typeid(T) Vrací identifikaci typu T typeid(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 typu const std::type_info & <typeinfo> Lze porovnávat na rovnost Má metodu name() vracející řetězec s nějakou formou jména typu

RTTI: 

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); /* ... */ }

RTTI: 

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); /* ... */ }

RTTI: 

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

Kanonické tvary tříd: 

Kanonické tvary tříd

Kanonické tvary tříd: 

Kanonické tvary tříd Neinstanciované třídy Policy classes Traits templates Třídy s jednou instancí Singletony Třídy instanciované jako proměnné Uživatelské datové typy, chytré ukazatele, ... Funktory a podobné parametry šablon Visitory a podobné objekty s virtuálními funkcemi Třídy instanciované dynamicky Plain Old Data Velká a sdílená data Třídy s hierarchií dědičnosti Poznámka: Toto třídění nemůže být ani úplné ani jednoznačné

Kanonické tvary tříd: 

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

Kanonické tvary tříd: 

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í šablona template< class T> class Stack { /* ... */ void push( typename type_traits::param_t x); }; Univerzální definice traits template< class T> struct type_traits { typedef const T & param_t; }; Explicitní specializace traits template<> struct type_traits< char> { typedef char param_t; };

Kanonické tvary tříd: 

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_;

Kanonické tvary tříd: 

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 &);

Kanonické tvary tříd: 

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 &);

Kanonické tvary tříd: 

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

Visitor: 

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 &); /* ... */ };

Visitor: 

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 &); /* ... */ };

Visitor: 

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 &); /* ... */ };

Visitor: 

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 &); /* ... */ };

Visitor: 

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 &); /* ... */ };

Visitor: 

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

Kanonické tvary tříd: 

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

Kanonické tvary tříd: 

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

Kanonické tvary tříd: 

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 &); };

Kanonické tvary tříd: 

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&); };

Kanonické tvary tříd: 

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 */; };

Design Patterns: 

Design Patterns Návrhové vzory

Design patterns: 

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

Design patterns: 

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, ...)

Design patterns: 

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"

Design patterns: 

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

Design patterns: 

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í

Design patterns: 

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

Design patterns: 

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

Design patterns: 

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

Šablony: 

Šablony Hlubší pohled

Šablony tříd – triky: 

Š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 ... */}

Šablony tříd – triky: 

Š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 ... */}

Šablony tříd – triky: 

Š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

Šablony tříd – triky: 

Š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...

Šablony tříd – triky: 

Š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...

Parciální specializace: 

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

Parciální specializace: 

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 */ };

Parciální specializace: 

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ů

Parciální specializace: 

Parciální specializace Typická použití parciální a explicitní specializace Vý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šší

Parciální specializace: 

Parciální specializace Typická použití parciální a explicitní specializace Vý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

Parciální specializace: 

Parciální specializace Typická použití parciální a explicitní specializace Vý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

Parciální specializace: 

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

Parciální specializace: 

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 objekty Obsahují 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

Traits & policies: 

Traits & policies Traits Šablony, ze kterých nejsou vytvářeny objekty Obsahují 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 classes Třídy, ze kterých obvykle nejsou vytvářeny objekty Př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

typename: 

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

Triky s šablonami: 

Triky s šablonami Porovnání typů s booleovským výstupem template< 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}; /* ... */ };

Triky s šablonami: 

Triky s šablonami Porovnání typů s typovým výstupem template< 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; /* ... */ };

Triky s šablonami: 

Triky s šablonami Kompilační ověření invariantu template< int x> struct AssertNot; template<> struct AssertNot< 0> { enum { value = true }; }; template< int x> struct Assert { enum { value = AssertNot< ! x>::value }; };

Triky s šablonami: 

Triky s šablonami Kompilační ověření invariantu template< 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]

Teoretický pohled na šablony: 

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

Teoretický pohled na šablony: 

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

Teoretický pohled na šablony: 

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

Teoretický pohled na šablony: 

Teoretický pohled na šablony Šablona třídy je kompilátorem vyhodnocovaná funkce f : Ti × Kj  Tm × Kn Taková funkce může být definována Jedním předpisem Základní šablonou Po částech Parciálními specializacemi šablony V jednotlivých bodech Explicitními specializacemi šablony

Teoretický pohled na šablony: 

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

Teoretický pohled na šablony: 

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];

Teoretický pohled na šablony: 

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

Teoretický pohled na šablony: 

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

Teoretický pohled na šablony: 

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

Teoretický pohled na šablony: 

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

***Nepoužité slajdy***: 

***Nepoužité slajdy***

Teoretický pohled na šablony: 

Teoretický pohled na šablony Triky s typovým konstrukcemi Seznam 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;

Teoretický pohled na šablony: 

Teoretický pohled na šablony Triky s typovým konstrukcemi Seznam 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;

Teoretický pohled na šablony: 

Teoretický pohled na šablony Triky s typovým konstrukcemi Seznam 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; };

Teoretický pohled na šablony: 

Teoretický pohled na šablony Triky s typovým konstrukcemi Seznam 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; };

Teoretický pohled na šablony: 

Teoretický pohled na šablony Triky s typovým konstrukcemi Seznam 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; };

Teoretický pohled na šablony: 

Teoretický pohled na šablony Triky s typovým konstrukcemi Jiná 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; };

Teoretický pohled na šablony: 

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ř

Exception-safe programming: 

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

Exception-safe programming: 

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

Exception-safe programming: 

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řit try-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; } }

Exception-safe programming: 

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řit try-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; } }

Oblasti platnosti identifikátorů : 

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

Ochrana přístupu : 

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

Preprocesor a překlad: 

Preprocesor a překlad

Spojování modulů: 

Spojování modulů

Spustitelný program a spuštěný proces: 

Spustitelný program a spuštěný proces

***** 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é konverze Drobná vylepšení Striktnější typová kontrola oproti C new a delete Komentáře Šablony Zpracování výjimek Knihovny Streams

Virtuální funkce – Specializace datové struktury: 

Virtuální funkce – Specializace datové struktury class 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); };

Virtuální funkce – Užití datové struktury: 

Virtuální funkce – Užití datové struktury class 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; };

Virtuální funkce - Řešení v C-stylu: 

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

Virtuální funkce - Řešení v C-stylu: 

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

Virtuální funkce - Řešení v C-stylu: 

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

Virtuální funkce - Řešení 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; }

Virtuální funkce - Řešení 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; }

Statické a dynamické volání virtuálních metod: 

Statické a dynamické volání virtuálních metod void 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) */ }

String č. 5 - operator [ ]: 

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); };   /* ... */ };

String č. 5 - operator [ ]: 

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

String č. 6 - Držadlo: 

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

String č. 6 - Abstraktní tělo: 

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

String č. 6 - Jednoduché tělo: 

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

String č. 6 - Jednoduché tělo: 

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

String č. 6 - Složené tělo: 

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

String č. 6 - Složené tělo: 

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(); }

String č. 6 - Složené tělo: 

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()); }

String č. 6 - Složené tělo: 

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

Problém: 

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

Řešení 1: 

Řešení 1 Nahradit rekurzi pomocnou strukturou a cyklem typedef 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(); } }

Řešení 1: 

Ř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); } };

Řešení 1: 

Řešení 1 Pomocná struktura typedef 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

Řešení 2: 

Ř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.

Problém: 

Problém Problém: Implementace mnoha funkcí je rekurzivní void StringBodyConcatenate::copy( char * dst) const { _a->copy( dst); _b->copy( dst + _a->length()); }

Řešení ?: 

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

Řešení ?: 

Ř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()); }

Řešení ?: 

Ř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); } }

Řešení ?: 

Ř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 pozici class 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; }

Řešení ?: 

Ř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 ); }

Řešení ?: 

Řešení ? Pyrrhovo vítězství: Rekurze je odstraněna Algoritmus má nyní kvadratickou složitost

Závěr: 

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

STL – vector: 

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()); /* ... */

STL – vector: 

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; /* ... */

STL – vector: 

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); /* ... */

STL – vector: 

STL – vector /* template<class T, class A = allocator<T> > class vector { ... */ protected: A allocator; };

STL – vector – naivní implementace – hlavička: 

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

STL – vector – naivní implementace – přístup: 

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

STL – vector – naivní implementace - konstrukce: 

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]); */ } }

STL – vector – naivní implementace – push/pop: 

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() { /* ... */ }

STL – vector – vylepšená implementace - konstrukce: 

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

STL – vector – naivní implementace – iterátory: 

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

STL – vector – naivní implementace – iterátory: 

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

Operátory new a delete : 

Operátory new a delete Operátory new a delete mohou 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);

Operátory new a delete: 

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 konstruktoru X * p = new X(a,b,c); (nekorektní) ekvivalent X * p = (X*)X::operator new(sizeof(X)); if (p) p->X::X(a,b,c); // zakázáno Vztah operátoru delete a destruktoru delete p; ekvivalent if (p) p->X::~X(); // povoleno X::operator delete((void*)p);

Šablony tříd – explicitní instanciace: 

Š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řídy template< 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>;

authorStream Live Help