Prezentace se nahrává, počkejte prosím

Prezentace se nahrává, počkejte prosím

Knihovny pro fyzikalni vypocty Milan Holec FJFI CVUT v Praze.

Podobné prezentace


Prezentace na téma: "Knihovny pro fyzikalni vypocty Milan Holec FJFI CVUT v Praze."— Transkript prezentace:

1 Knihovny pro fyzikalni vypocty Milan Holec FJFI CVUT v Praze

2 Fortran Fortran je nejstarsi dosud stale pouzivany jazyk.Pravdepodobne diky rychlosti,vykonu a dostupnosti osvedcenych a rozsahlych knihoven.

3 SYNTAXE FORTRANU Na ukazku uplne jednoduchy program, ktery jen nacte ze vstupu 3 hodnoty ve stupnich Celsia,doplni dalsich 6 a nakonec vypise 9 hodnot ve Fahrenheitove stupnici. V 1. casti je deklarace promenych V 2. casti je nacteni hodnot ze vstupu Ve 3. casti je doplneni hodnot a konverze na Fahren. Ve 4. casti je vypsani hodnot na obrazovku KOMENTAR: U druheho cyklu je pouzita tzn. “navest” a prikaz continue program Convert implicit none ! Deklarace real*4 tempC(9)/9*0/, tempF(1:9), K integer*2 ZERO_SHIFT, i, j parameter (ZERO_SHIFT = 32,K = 9./5.) ! Input write(*,*) “3X teplota v Celsius..." do i=1,9,4 read*, tempC(i) end do ! Compute do i=2,4 j = i + 4 tempC(i) = (tempC(i-1) + tempC(i+1))/2 tempC(j) = (tempC(j-1) + tempC(j+1))/2 end do !forall(i=1:9:2) tempC(i) = tempC(10-i) do 100, i=1,9 tempF(i) = K * tempC(i) + ZERO_SHIFT ! Output print*, “Odpovidajici konverze je” print*, tempF(i), “F stupnu“ 100 continue end

4 FFT Knihovna pro rekursivni Fourierovu transformaci

5 ALGORITMUS FFT Takto vypada diskretni F transformace Jeji narocnost je O(N^2). Vyuzitim Cooley-Tukey algoritmu,ktery je zalozen na trhani sumy po lichych a sudych clenech se da narocnost snizit az na O(n*lnN). Podminkou je jen rozdeleni def.oboru fce na N=2^m dilku(koeficientu) 100 J = 1 N1 = N - 1 DO 104, I = 1, N1 IF ( I.GE. J ) GOTO 101 XT = X(J) X(J) = X(I) X(I) = XT 101 K = N / IF ( K.GE. J ) GOTO 103 J = J - K K = K / 2 GOTO J = J + K 104 CONTINUE Kde X(J) je prvni cast sumy a X(I) je druha cast sumy. Na ne se opet rekurzivne aplikuje deleni na dve casti

6 STANDARTNI KNIHOVNY BLAS prace se skalary,vektory,maticemi a tenzory EISPACKnastroj na vypocet vlastnich cisel a vektoru matic(complex,real,hermit) LINPACKmetoda nejmensich ctvercu,soustavy lin.rovnic,faktorizace matic LAPACKmodifikovana kombinace EISPACK+LINPACK na parallelni architektury MINPACKreseni soustav nelinearnich rovnic,metoda nejmensich ctvercu MUDPACKdifferencni metody reseni linearnich 3D eliptickych parcialnich dif.rovnic NCARMatematicke a statisticke knihovny ODEPACKreseni ODE stiff I non-stiff typu krokovou a Gearovou metodou SLATECsoubor matematickych a statickych rutin SPECFUNBesselova a Gamma fce,chybove fce,exponencialni integraly TOMSrobustni komplexni balik rozmanitych tipu knihoven a subrutin

7 L-M interaction Maxwell-Schrödinger Model Interakce latky a laseru a propagace zareni Metodou site

8 L-M interaction Intensivni ultra-kratky laserovy paprsek interaguje s vodikovym plynem H 2 + ve vakuu Model popisuje sireni radiacniho zareni plynem. Z fyzikalniho hlediska jde o polarizaci molekul el.polem. Tim jsou svazany Maxwellovi makroskopicke rovnice s Schrödingerovou rovnici dynamiky Kde P(r`,t) je polarizace molekuly

9 SCHEMA EXPERIMENTU

10 SCHEMA POLARIZACE

11 RESONANCE Pro zkoumani sireni el.vlny je dulezita resonance cyklotronove frekvence elektronu v potencialu jadra.Kdyz laserovym pulsem spravne excitujeme elektron dochazi k ionizaci... A naslednym navedenim elektronu zpet k jadru se emituje zareni o nekolikanasobne frekvenci nez puvodni...

12 DIFFERENCE VELICIN Pouzite vztahy pro differencni metodu parcialnich diff.rovnic Z vyjadreni derivace polarizace ziskame z Yee schematu zavislost el.intenzity Vlnovou fci muzeme dle Crank-Nicolsonova schematu zapsat

13 NUMERICKA METODA Pres slozitost sparovani makroskopickych rovnic el.mag. pole a kvantove mechanicke rovnice je uloha resena pouze jednoduchou differencni metodou.Cilem je vyjadrit co nejpresneji Polarizaci Musime resit odpovidajici pocet parcialnich diff. rovnic dle poctu bunek

14 REFERENCE standartni knihovny ke stazeni + priklady a implementace Strucny guide pro zaklady fortranu a compilator pro windows Home web knihoven pro Fast Fourier Transformation Obecne informace o cemkoliv Robustni databaze knihoven napsanych vetsinou ve fortranu

15

16

17

18

19

20

21

22

23

24

25

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

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

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

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

30 STL – Ostatní  - užitečné algoritmy (for_each, sort, next_permutation,...)  - podpora funktorů  - podpora iterátorů  - alokátory pro kontejnery  - jednoduchá matematika na prvcích kontejnerů  - pomocné konstrukce (pair,...)

31 STL – Algoritmy  Šablona funkce for_each  template 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

32 STL – Algoritmy  Šablona funkce for_each  template 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

33 STL – Algoritmy  Použití funkce for_each void my_function( double & x) { x += 1; } void increment( list & c) { for_each( c.begin(), c.end(), my_function); }

34 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 & c, double value) { for_each( c.begin(), c.end(), my_functor( value)); }

35 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 & c) { my_functor f; f = for_each( c.begin(), c.end(), f); return f.s; }

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

37 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 & c) { return for_each( c.begin(), c.end(), my_functor()).s; }

38 STL – Algoritmy  Šablona funkce find  template InputIterator find( InputIterator first, InputIterator last, const T & value) { for (; first != last; ++first) if ( * first == value ) break; return first; }

39 STL – Algoritmy  Šablona funkce find_if  template 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

40 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

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

42 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

43 STL – Algoritmy  Přehled algoritmů  Pozor: Algoritmy neprodlužují ani nezkracují kontejner vector 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"  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));

44 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

45 iostream vstupní a výstupní proudy

46 iostream #include 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

47 stringstream #include using namespace std; string f( int a) { ostringstream x; x << "a = " << a; return x.str(); }  - *stringstream – spolupracuje se std::string  - *strstream – spolupracuje s char *

48 iostream  Hlavičkové soubory  - souborový vstup a výstup (ifstream, ofstream, fstream)  - manipulátory pro nastavení parametrů formátovaného vstupu a výstupu (setw, setprecision, setfill, setbase,...)  - základní funkce abstraktního souboru, základní nastavení formátu (hex, left,...)  - standardní vstup a výstup (cin, cout, cerr)  - abstraktní vstupní a kombinované médium (istream, iostream)  - abstraktní výstupní médium (ostream)  - vnitřní paměť jako médium (istringstream, ostringstream, stringstream)

49 iostream  Abstraktní rozhraní  basic_... 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 basic_istream basic_ostream basic_iostream basic_streambuf 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

50 iostream  Abstraktní rozhraní  funkce, které potřebují zapisovat, dostávají basic_ostream & void zapis_neco( ostream & o) { o << neco; } ios_base basic_ios basic_ostream nastavení formátovače stav média přímý a formátovaný zápis dědičnost virtuální dědičnost

51 iostream  Abstraktní rozhraní  funkce, které potřebují číst, dostávají basic_istream & pozor: čtení modifikuje stream (posunuje ukazovátko) void cti_neco( istream & i) { i >> neco; } ios_base basic_ios basic_istream nastavení formátovače stav média přímé a formátované čtení dědičnost virtuální dědičnost ukazatel

52 iostream  Abstraktní rozhraní  funkce, které potřebují číst i zapisovat totéž médium, dostávají basic_iostream & 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 basic_istream basic_ostream basic_iostream 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

53 iostream  Abstraktní rozhraní  basic_iostream obsahuje funkce čtení i zápisu  nastavení formátu i stav média jsou společné pro čtení i zápis  basic_ios zde musí být pouze v jedné instanci  dědění basic_ios musí být virtuální template class basic_istream : virtual public basic_ios ios_base basic_ios basic_istream basic_ostream basic_iostream 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

54 iostream  Médium  Konkrétní médium je implementováno jako potomek třídy basic_streambuf  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 basic_istream basic_ostream basic_iostream basic_...buf basic_streambuf 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

55 sstream  Paměťové médium  Obálkové třídy...stringstream zařídí vznik basic_stringbuf  Umožňují přístup k médiu jako basic_string ios_base basic_ios basic_istream basic_ostream basic_iostream basic_stringbuf basic_istringstream basic_ostringstream basic_stringstream basic_streambuf 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

56 fstream  Souborové médium, (cin, cout, cerr)  Obálkové třídy...fstream zařídí vznik basic_filebuf  Soubor se otevírá(zavírá) metodou open(close) těchto tříd ios_base basic_ios basic_istream basic_ostream basic_iostream basic_filebuf basic_ifstream basic_ofstream basic_fstream basic_streambuf 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 Operační systém

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

58 iostream  formátované čtení basic_istream & operator>>( basic_istream & 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 & operator & 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;

59 iostream  formátované čtení/zápis basic_istream & operator>>( basic_istream & s, D & x) basic_ostream & operator & 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

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

61 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

62 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

63 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

64 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

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

66 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 & operator<<( basic_ostream & s, ios_base & (* f)( ios_base & s)) { f( s); return s; }

67 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 & operator<<( basic_ostream & s, const setw_manip & p) { s.width( p.x); return s; }

68 iostream  Vztah proudů a kontejnerů  stringstream umožňuje přístup k médiu jako string  pro string jsou definovány operátory >  definuje iterátory nad proudy: istream_iterator  vstupní iterátor, čte typ U pomocí operátoru >>  parametrem konstruktoru je istream & ostream_iterator  výstupní iterátor, zapisuje typ U pomocí operátoru <<  parametrem konstruktoru je ostream &

69 iostream  Iterátory nad proudy - příklady  naplnění kontejneru ze vstupu std::vector a; std::copy( std::istream_iterator ( std::cin),// aktuální pozice std::istream_iterator (),// "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 b; std::copy( b.begin(), b.end(), std::ostream_iterator ( std::cout));

70 Exception handling Mechanismus výjimek

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

72 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:  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; }

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

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

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

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

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

78 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

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

80 Exception handling  Standardní výjimky   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

81 Exception handling  Standardní výjimky   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

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

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

84 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

85 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

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

87 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

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

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

90 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

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

92 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

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

94 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

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

96 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

118 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  "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 String StringStack::pop() { if ( ! top_ ) throw StackEmpty(); std::auto_ptr 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é

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

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

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

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

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

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

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

126 Exception specifications  Exception specifications  Kompilátor (a runtime) zajistí, že nepovolená výjimka neopustí funkci  Microsoft Visual C 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

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

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

129 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

130 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 (e) static_cast (e) reinterpret_cast (e)

131 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 (e) static_cast (e) reinterpret_cast (e)  Novinka - přetypování s běhovou kontrolou: dynamic_cast (e)

132 Přetypování const_cast (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 ( this)->references++; } private: /*... data... */ int references; };  Jiný příklad: datové struktury s amortizovaným vyhledáváním

133 Přetypování const_cast (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; };

134 Přetypování static_cast (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

135 Přetypování static_cast (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 ( this)); } };

136 Přetypování static_cast (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

137 Přetypování static_cast (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 ( p); /*... */ } break; case T_Y: { Y * yp = static_cast ( p); /*... */ } break; }

138 Přetypování reinterpret_cast (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 ( & d), sizeof( double)); } Obsah souboru je nepřenositelný

139 Přetypování dynamic_cast (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

140 Přetypování dynamic_cast (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 ( p); if ( xp ) { /*... */ } Y * yp = dynamic_cast ( p); if ( yp ) { /*... */ }

141 RTTI Typová informace za běhu

142 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 &   Lze porovnávat na rovnost  Má metodu name() vracející řetězec s nějakou formou jména typu

143 RTTI  Typické použití  Alternativa místo dynamic_cast #include 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 ( p); /*... */ } if ( typeid( * p) == typeid( Y) ) { Y * yp = static_cast ( p); /*... */ }

144 RTTI  Typické použití  Alternativa místo dynamic_cast  Pozor: rovnost typeid nereflektuje dědičnost Zatímco dynamic_cast ano #include 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 ( p); /*... */ } if ( typeid( * p) == typeid( Z) ) // platí { Z * zp = static_cast ( p); /*... */ }

145 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

146 Kanonické tvary tříd

147  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é

148 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 struct type_traits; template<> struct type_traits { typedef char param_type; enum { min = 0, max = 255 }; static bool less( char, char); };

149 Kanonické tvary tříd  Policy class – použití  Univerzální šablona template 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_tree;  Traits template – použití  Deklarace traits template struct type_traits;  Univerzální šablona template class Stack { /*... */ void push( typename type_traits::param_t x); };  Univerzální definice traits template struct type_traits { typedef const T & param_t; };  Explicitní specializace traits template<> struct type_traits { typedef char param_t; };

150 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 pinst_; }; /* syslog.cpp */ std::auto_ptr SysLog::pinst_;

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

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

153 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 v; for_each( v.begin(), v.end(), Printer( std::cout));

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

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

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

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

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

159 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 my_vector_; my_vector_ elements_; };

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

161 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

162 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 nodes_; multimap edges_; map colors_; ColoredGraph(const ColoredGraph &); void operator=(const ColoredGraph &); };

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

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

165 Design Patterns Návrhové vzory

166 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

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

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

169 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

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

171 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

172 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

173 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

174 Šablony Hlubší pohled

175 Šablony tříd – triky  Specializace metody pro konkrétní typ  Překrývá generické tělo template class X { /*... */ void f(/*...*/); }; template void X ::f(/*...*/) { /*... generické tělo pro libovolný typ T... */} template<> void X ::f(/*...*/) { /*... speciální tělo pro typ int... */}

176 Šablony tříd – triky  Specializace metody pro konkrétní typ  Překrývá generické tělo template<> void X ::f(/*...*/) { /*... speciální tělo pro typ int... */}  Starší verze C++ zde nepoužívají prefix template<> void X ::f(/*...*/) { /*... speciální tělo pro typ int... */}

177 Šablony tříd – triky  Specializace metody pro konkrétní typ  Překrývá generické tělo template<> void X ::f(/*...*/) { /*... speciální tělo pro typ int... */}  Tato konstrukce se nazývá  Explicitní specializace šablony funkce

178 Šablony tříd – triky  Specializace metody pro konkrétní typ  Překrývá generické tělo template<> void X ::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...

179 Šablony tříd – triky  Specializace metody pro konkrétní typ  Překrývá generické tělo template<> void X ::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...

180 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 C; // základní deklarace template class C { // specializace bool cmp( P *, Q *); }; template class C : public Z { // jin á specializace bool set( Z &); }; template class C { // základní definice X add( Y); };

181 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 C { /* specializace pro dvě pole stejné velikosti */ };

182 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 C { /* specializace pro dvě pole stejné velikosti */ };  Krajním případem parciální specializace je explicitní specializace  Explicitní specializace template<> class C { /*... */ };  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ů

183 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 může být jednodušší

184 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 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 nedovoluje vytvořit ukazatel na jeden prvek

185 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 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 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 volá šablonu char_traits, ve které je např. definována porovnávací funkce

186 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 volá šablonu char_traits, ve které je např. definována porovnávací funkce template struct char_traits; template class basic_string { /*... */ int compare( const basic_string & b) const { /*...*/ char_traits ::compare( /*... */) /*...*/ } }; template<> struct char_traits { /*... */ static int compare(const char* s1, const char* s2, size_t n) { return memcmp( s1, s2, n); } };

187 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 doplňuje informace o typu T, např. porovnávací funkci

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

189 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 není známo, co znamená char_traits ::compare  Problém se týká tzv. závislých jmen, obsahujících jméno parametru šablony Příklady: T::x, C, C ::x  K úspěšné syntaktické analýze je v C++ nutné odlišit jména typů od ostatních jmen: bflm ::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 ::psvz( * x); // deklarace Nesmí se používat v deklaraci předků šablony Nesmí se používat mimo šablony

190 Triky s šablonami  Porovnání typů s booleovským výstupem template struct Equal { enum { value = false }; }; template struct Equal { enum { value = true }; };  Equal ::value je konstantní výraz  Použití template class Test { enum { T1_is_int = Equal ::value}; enum { T1_is_long = Equal ::value}; /*... */ };

191 Triky s šablonami  Porovnání typů s typovým výstupem template struct IfEqual { typedef D Result; }; template struct Equal { typedef C Result; };  IfEqual ::Result je typ Význam: X == Y ? U : V  Použití template class Test { typedef Equal ::Result longT1; /*... */ };

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

193 Triky s šablonami  Kompilační ověření invariantu template struct AssertNot; template<> struct AssertNot { enum { value = true }; }; template struct Assert { enum { value = AssertNot ::value }; };  Použití template class Array { enum { check = Assert 0)>::value }; /*... */ }; Array x; error C2027: use of undefined type 'AssertNot ' with [x=1]

194 Teoretický pohled na šablony  Šablona třídy je kompilátorem vyhodnocovaná funkce  f : T i  T  T je množina všech typů zkonstruovatelných v jazyce C

195 Teoretický pohled na šablony  Šablona třídy je kompilátorem vyhodnocovaná funkce  f : T i  T  T je množina všech typů zkonstruovatelných v jazyce C  f : T i × K j  T  šablona s celočíselnými parametry  K je množina všech celočíselných konstant

196 Teoretický pohled na šablony  Šablona třídy je kompilátorem vyhodnocovaná funkce  f : T i  T  T je množina všech typů zkonstruovatelných v jazyce C  f : T i × K j  T  šablona s celočíselnými parametry  K je množina všech celočíselných konstant  f : T i × K j  T m × K n  výsledná třída může obsahovat typy a konstanty

197 Teoretický pohled na šablony  Šablona třídy je kompilátorem vyhodnocovaná funkce  f : T i × K j  T m × K n  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

198 Teoretický pohled na šablony  Příklad template struct Fact { enum { value = Fact ::value * N }; }; template<> struct Fact { enum { value = 1 }; };

199 Teoretický pohled na šablony  Příklad template struct Fact { enum { value = Fact ::value * N }; }; template<> struct Fact { enum { value = 1 }; };  Použití enum { N = 10 }; int permutations[ Fact ::value];

200 Teoretický pohled na šablony  Příklad template struct Fact { enum { value = Fact ::value * N }; }; template<> struct Fact { enum { value = 1 }; };  Kontrolní otázka:  Kolik je Fact ::value

201 Teoretický pohled na šablony  Příklad template struct Fact { enum { value = Fact ::value * N }; }; template<> struct Fact { enum { value = 1 }; };  Kontrolní otázka:  Kolik je Fact ::value  MS Visual C++ 7.1: fatal error C1202: recursive type or function dependency context too complex  Řetěz instanciací Fact, Fact, Fact,... způsobí přetečení tabulek kompilátoru

202 Teoretický pohled na šablony  Jiný příklad template struct Fib { enum { value = Fib ::value + Fib ::value }; }; template<> struct Fib { enum { value = 1 }; }; template<> struct Fib { enum { value = 1 }; };  Kontrolní otázka:  Jak dlouho trvá výpočet (tj. kompilace) Fib ::value

203 Teoretický pohled na šablony  Jiný příklad template struct Fib { enum { value = Fib ::value + Fib ::value }; }; template<> struct Fib { enum { value = 1 }; }; template<> struct Fib { enum { value = 1 }; };  Kontrolní otázka:  Jak dlouho trvá výpočet (tj. kompilace) Fib ::value  MS Visual C++ 7.1: Build Time 0:00  Kompilátory ukládají již vytvořené instanciace a nepočítají je znovu

204 ***Nepoužité slajdy***

205 Teoretický pohled na šablony  Triky s typovým konstrukcemi  Seznam typů template struct List { typedef H Head; typedef R Rest; }; struct EmptyList {};  Použití typedef List< char *, List< const char *, List< std::string, EmptyList> > > StringTypes;

206 Teoretický pohled na šablony  Triky s typovým konstrukcemi  Seznam typů template 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;

207 Teoretický pohled na šablony  Triky s typovým konstrukcemi  Seznam typů template struct List { typedef H Head; typedef R Rest; }; struct EmptyList {};  Funkce na seznamu typů template struct First { typedef typename L::Head Result; };

208 Teoretický pohled na šablony  Triky s typovým konstrukcemi  Seznam typů template struct List { typedef H Head; typedef R Rest; }; struct EmptyList {};  Funkce na seznamu typů template struct First { typedef typename L::Head Result; }; struct NullType {}; template<> struct First { typedef NullType Result; };

209 Teoretický pohled na šablony  Triky s typovým konstrukcemi  Seznam typů template struct List { typedef H Head; typedef R Rest; }; struct EmptyList {};  Funkce na seznamu typů template struct Nth { typedef typename Nth ::Result Result; }; template struct Nth { typedef typename L::Head Result; };

210 Teoretický pohled na šablony  Triky s typovým konstrukcemi  Jiná implementace seznamu typů template struct List; struct EmptyList;  Funkce na seznamu typů template struct Nth; template struct Nth, n> { typedef typename Nth ::Result Result; }; template struct Nth, 0> { typedef H Result; };

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

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

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

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

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

216 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

217 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

218 Preprocesor a překlad Code... push call... call... ret Import _printf _getch Export _main Data ‘H’,’e’,’l’,’l’, ’o’,10,0 hello.o hello.i /*...*/ int printf( const char *,...); /*...*/ int getch(); int putch(); /*...*/ int main( int argc, char * * argv) { printf( “Hello\n”); getch(); return 0; } stdio.h /*...*/ int printf( const char *,...); /*...*/ hello.c #include int main( int argc, char * * argv) { printf( “Hello\n”); getch(); return 0; } conio.h int getch(); int putch(); /*...*/

219 Spojování modulů Code... push call... call... ret Import _printf _getch Export _main Data ‘H’,’e’,’l’,’l’, ’o’,10,0 hello.o Code... call... call Import _main _exit Export entry point Data cstart.o Code... Import Export _printf Data printer.o Code... Import Export _getch Data creader.o Code syscall... Import Export _exit Data exit.o

220 Spustitelný program a spuštěný proces... push call... call... ret ‘H’,’e’,’l’,’l’, ’o’,10,0 hello... call... call entry point... syscall... Code Data Code Data Heap Stack Adresový prostor IP R0 R1... FFFFFFFF

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

239 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 length() ? _a->read_at( i) : _b->read_at( i - _a->length()); } void StringBodyConcatenate::write_at( int i, char ch) { i length() ? _a->write_at( i, ch) : _b->write_at( i - _a->length(), ch); }

240 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 < ; i++ ) x = x + "a"; }

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

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

243 Řešení 1  Pomocná struktura typedef std::stack 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

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

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

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

247 Řešení ?  Problém: Implementace funkce copySomething je stále rekurzivní int StringBodyConcatenate::copySomething( char * dst, int offset) const { if ( offset length() ) return _a->copySomething( dst, offset); else return _b->copySomething( dst + _a->length(), offset - _a->length()); }

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

249 Ř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 length() ) return _a; else return _b; }

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

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

252 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

253 STL – vector template > 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()); /*... */

254 STL – vector /* template > 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; /*... */

255 STL – vector /* template > 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); /*... */

256 STL – vector /* template > class vector {... */ protected: A allocator; };

257 STL – vector – naivní implementace – hlavička template class oriented_iterator; template class const_oriented_iterator; template 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 iterator; typedef const_oriented_iterator const_iterator; typedef oriented_iterator reverse_iterator; typedef const_oriented_iterator const_reverse_iterator; /*... */ private: size_type _n; T * _p; };

258 STL – vector – naivní implementace – přístup template reference vector ::at(size_type pos) { return pos >= 0 && pos < n ? _p[ pos] : _dummy(); } template const_reference vector ::at(size_type pos) const { return pos >= 0 && pos < n ? _p[ pos] : _dummy(); } template reference vector ::operator[](size_type pos) { return at( pos); } template const_reference vector ::operator[](size_type pos) const { return at( pos); } template reference vector ::dummy() const { return *(T*)0; }

259 STL – vector – naivní implementace - konstrukce template vector ::vector() { _n = 0; _p = 0; } template vector ::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 vector ::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]); */ }

260 STL – vector – naivní implementace – push/pop template void vector ::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 void vector ::pop_back() { /*... */ }

261 STL – vector – vylepšená implementace - konstrukce void * operator new( size_t s, void * p) { return p; } template vector ::vector( size_type n, const T & v) { _n = n; _p = reinterpret_cast ( new char[ _n * sizeof( T)]); for ( int i = 0; i < _n; i++) { new( _p[ i]) T( v); } template vector ::~vector() { for ( int i = 0; i < _n; i++) { _p[ i]->T::~T(); } delete[] reinterpret_cast ( _p); }

262 STL – vector – naivní implementace – iterátory template class oriented_iterator { public: T & operator *() const { return _i _n ? *( T *)0 : _v->_p[ _i]; } const oriented_iterator & operator ++() /* prefix */ { if ( _i _n ) _i++; return * this; } oriented_iterator operator ++( int) /* postfix */ { oriented_iterator old = * this; operator ++(); return old; } bool operator ==( const oriented_iterator & b) const { return _i == _b->_i; } private: friend class vector ; oriented_iterator( vector * v, int i) { _v = v; _i = i; } vector * _v; int _i; };

263 STL – vector – naivní implementace – iterátory template iterator vector ::begin() { return iterator( this, 0); } template const_iterator vector ::begin() const { return const_iterator( this, 0); } template iterator vector ::end() { return iterator( this, _n); } template iterator vector ::end() const { return const_iterator( this, _n); }

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

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

266 Š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 X { /*... */ void f(/*...*/); };  Nepublikovaný hlavičkový soubor XBody.h  Generická těla metod #include "X.h" template void X ::f(/*...*/) { /*...*/ }  Knihovní modul XBodyInt.cpp  Instanciace pro typ int #include "XBody.h" template X ;

267


Stáhnout ppt "Knihovny pro fyzikalni vypocty Milan Holec FJFI CVUT v Praze."

Podobné prezentace


Reklamy Google