Šablony funkcí a tříd Šablony v C++ jsou vzory, podle kterých může překladač vytvářet celé skupiny podobných tříd či funkcí, které se liší jen v některých datových typech nebo hodnotách konstant. Deklarace šablony : template deklarace; kde „deklarace“ může obsahovat běžnou deklaraci třídy nebo funkce. Šablona se uvádí klíčovým slovem template po němž následuje v závorkách <> seznam formálních parametrů šablony a pak deklarace dané třídy nebo funkce. Deklarace šablony končí středníkem. Instance šablony tvoříme implicitně (použitím) nebo explicitně, příkazem template instance; Parametry šablony mohou být hodnotové nebo typové, ty pak předepisujeme pomocí klíčového slova class nebo typename. Šablony se dají přirovnat k makrům v ANSI C – jedná se v podstatě o textové nahrazování formálních parametrů v těle šablony skutečnými parametry, vše se děje ještě na úrovni překladače
Šablony funkcí a tříd Příklady: Šablona třídy „Cosi“: template class Cosi { /* … */ }; Šablona funkce vracející větší z parametrů typu T: template T max( T x, T y) { return ( x > y ? x : y ); } Pomocí šablon se dají vytvořit tzv. generické třídy Použití šablony: za parametr šablony se dosadí konkrétní typ/hodnota Příklad: čítač pro různé datové typy template class Citac { T Hodn; … }; … Citac icitac(0) Citas zcitac(’A’) … int vi=iciatac.getValue(); int vz=zcitac.getValue();
Šablony funkcí a tříd Definice metod třídy Citac: template void Citac ::increment() { Hodn++; } template T Citac ::GetValue() { return Hodn; } Definice metod generické třídy musí opět začít generickou hlavičkou Tyto metody musí být uvedeny v hlavičkovém souboru Generická třída může být odvozena z abstraktní třídy Parametrem šablony může být i konstanta : template Příklad třídy pole: template class Pole { T slozky[D]; public : Pole(); Pole operator+(const Pole& p) const; T operator*( const Pole& p) const; Pole operator*(T s) const; friend Pole operator*(T s, const Pole& p); friend ostream& operator<<(ostream & o, const Pole& p); }
Šablony funkcí a tříd Pro každou velikost pole se vytvoří jiná definice třídy podle dané šablony. Nemusím tudíž brát ohled na různé velikosti polí při sčítání a násobení. Metoda součtu dvou polí : (musím si definovat =,+ a kopírující konstruktor) template Pole Pole::operator+(const Pole& p) const { Pole x; for (int i = 0; i < D; i++) { x.slozky[i] = slozky[i] + p.slozky[i]; } return x; } Příklad třídy pole: template class Pole { T slozky[D]; public : Pole(); Pole operator+(const Pole& p) const; T operator*( const Pole& p) const; Pole operator*(T s) const; friend Pole operator*(T s, const Pole& p); friend ostream& operator<<(ostream & o, const Pole& p); }
Vícenásobná dědičnost Pokud potřebuji aby potomek měl vlastnosti více předků Mohu předefinovávat metody předků. Všechny předky musím uvést v hlavičce. Př : class potomek : public predek1, public predek2, … { … }; Pokud předkové obsahují proměnné se stejným jménem pak dojde ke kolizi, musím k nim přistupovat pomocí specifikátoru rozsahu. Vícenásobnému zdědění položek se stejným jménem se dá zabránit pouze pomocí specifikace virtuální báze Př : metody vícenásobného dědění struct A { int a; }; struct B : A { int b; }; struct C : A { int c; }; struct A struct B struct C struct D : B, C {} Třída D obsahuje 2x složku a, jednou z třídy B podruhé z třídy C. dědění stejných položek lze zabránit pomocí virtuální báze - stejné položky se pak dědí jen jednou Př : struct B : virtual A { … }; struct C : virtual A { … }; struct D : A, B { … }; // teď je tam položka A::a jen jednou A::a B::bA::aC::c A::aB::bA::aC::c
Namespaces #include namespace RadimuvProstor { int secti(int a, int b); class Trida { private: int Atribut; public: void metoda(); }; void Trida::metoda() { cout << "Ahoj" << endl; } } int RadimuvProstor::secti(int a, int b) { return a + b; } slouží k odlišení názvů funkcí v rámci větších projektů pro přístup do určitého namespace se používá operátor :: nebo klíčové slovo „using“ implicitní – globální – namespace nemá žádné jméno, dá se na něj přistoupit např. takto: “::main()”
Výjimky mechanismu výjimek je definován k usnadnění ošetření chybových stavů v programu (např. dělení nulou, odkaz přes null pointer atd.) blok, ve kterém je riziko vzniku výjimky, se uzavře do bloku „try“ a ošetření výjimek se pak probíhá v navazujících blocích „catch“ Deklace metod, které mohou vyvolat výjimku, používá klíčová slovo „throw“ následované seznamem možných výjimek Pokud metoda v deklaraci nemá „throw“, znamená to, že může vyvolat jakoukoli výjimku (pozor, v Javě je to naopak – tam metoda bez „throw“ nevyvolává žádné výjimky!) Metoda může vyvolávat i více výjimek Třídy výjimek je vhodné tvořit v dědické hiearchii Opustí-li výjimka tělo naší funkce main, dojde k zavolání funkce terminate. Modifikace tohoto chování = pomocí funkce set_terminate. Funkce má jako parametr ukazatel na funkci, kterou má vyvolat volání funkce terminate. terminate i set_terminate jsou deklarovány v hlavičkovém souboru exception. Je-li z těla funkce nebo metody vyvržená výjimka, která není v seznamu výjimek, dojde k zavolání funkce unexpected. Tato funkce implicitně zavolá funkci terminate.Modifikace - funkce set_unexpected
Výjimky #include using namespace std; class Vyjimka { /* Třída výjimek */ private: string Duvod; public: void nastav(string d) { Duvod = d; } string dej() { return Duvod; } }; ostream &operator<< (ostream &os, Vyjimka &v) {return os << v.dej() << endl;} class Zlomek{ private: int C,J; public: void nastavCitatel(int c) { C = c;} void nastavJmenovatel(int j) {J = j;} double vydel() throw (Vyjimka); };
Výjimky double Zlomek::vydel() throw (Vyjimka) { // začátek bloku 2 int *i = new int; if (J == 0) { //začátek bloku 1 string s("Nejde"); Vyjimka v; v.nastav(s); throw v; } // konec bloku 1 delete i; return ((double)C / J); } // konec bloku 2 int main(void){ Zlomek z1,z2; z1.nastavCitatel(10); z2.nastavCitatel(5); for(int i = 5; i > -5; i--){ z1.nastavJmenovatel(i); z2.nastavJmenovatel(i); try{ cout << "10 / " << i << " = " << z1.vydel() << endl; cout << "5 / " << i << " = " << z2.vydel() << endl;} catch (Vyjimka v) { cout << v << endl; } catch (Jina_vyjimka j) {....} }return 0; }
Název kontejneru Typ kontejneru Hlavičkový soubor Popis kontejneru bitset posloupno st bitsetPosloupnost bitů pevné délky. deque posloupno st deque Oboustranná fronta. Prvky lze vkládat, nebo odebírat z obou konců. Sice lze rovněž odebírat, nebo vkládat prvky na libovolné místo ve frontě (kontejner deque to umožňuje), ale tato operace není příliš efektivní. list posloupno st listOboustranně zřetězený seznam. map asociativní kontejner map Asociativní pole. pole, které nemusí být indexováno celočíselným typem, ale čímkoliv. Třeba řetězcem. Pro daný klíč může existovat pouze 1 asociovaná hodnota. Tomuto kontejneru se budeme v budoucnu zabývat v samostatném článku. multimap asociativní kontejner map Asociativní pole. Pro daný klíč (index) může existovat více asociovaných hodnot. Tomuto kontejneru se budeme v budoucnu zabývat v samostatném článku. multiset asociativní kontejner set Multimnožina. množina, ve které se mohou prvky opakovat. Tomuto kontejneru se budeme věnovat později v samostatném článku. Knihovna STL
priority_ queue posloupnostqueue Prioritní fronta. Fronta, ve které neplatí pravidlo "první dovnitř, první ven". Prvky, které se do fronty uloží jsou uspořádány podle nějaké relace. Dalo by se říci, že předbíhají ve frontě podle nějaké předem dané priority. queueposloupnostqueue Klasické fronta. platí pravidlo, že prvek, který byl jako první vložen do fronty, z ní bude také první vybrán. set asociativní kontejner set Množina. Daná hodnota může být v množině obsažena jen jednou. Tomuto kontejneru se budeme věnovat později v samostatném článku. stackposloupnoststack Klasický zásobník. Platí pravidlo, že prvek, který byl vložen do zásobníku jako poslední bude vybrán jako první. vectorposloupnostvector Obdoba jednorozměrného pole. Tomuto kontejneru se budeme věnovat později v samostatném článku. Knihovna STL
Knihovna STL - bitset } #include int main( ) { // Using the default constructor using namespace std; bitset b0; cout b0 is: ( " b1 ( 6 ); cout b1( 6 ) is: ( " b2; cout b2 is: ( " b3 ( 6 ); cout b3( 6 ) is ( " b4 ( bitval4 ); cout b4( bitval4 ) is ( " b5 ( bitval5, 3, 6 ); cout b5( bitval, 3, 6 ) is ( " b6 ( bitval5, 3, 5 ); cout b6( bitval5, 3, 5 ) is ( " b7 ( bitval5, 2 ); cout b7( bitval, 2 ) is ( " << b7 << " )." << endl; } Output The set of bits in bitset b0 is: ( 00 ). The set of bits in bitset b1( 6 ) is: ( ). The set of bits in bitset b2 is: ( ). The set of bits in bitset b3( 6 ) is ( 110 ). The set of bits in bitset b4( bitval4 ) is ( ). The set of bits in bitset b5( bitval, 3, 6 ) is ( ). The set of bits in bitset b6( bitval5, 3, 5 ) is ( ). The set of bits in bitset b7( bitval, 2 ) is ( ).
Knihovna STL - bitset Erastotenovo síto čísla z rozsahu, ve kterém chceme zjistit všechna prvočísla, bereme jako prvky množiny kvůli paměťové efektivitě je pro množinu vhodné použít template bitset na začátku všechna čísla v množině označíme jako prvočísla (v množině bitset nastavíme bity na odpovídajících pozicích na true) pak ve smyčce bereme od nejmenšího všechna prvočísla, o kterých víme, že jsou prvočísla, (0 a 1 nebrat!) a „odznačujeme“ všechny jejich násobky v daném rozsahu tzn. postupně "prosíváme" množinu tak, že nakonec čísla složená (neprvočísla) zůstanou „pod sítem“ a prvočísla „nad sítem“. Prvocislo := 2; // zacnu od prvocisla 2 while 2 * Prvocislo <= Rozsah do begin Nasobek := 2 * Prvocislo; // prvni nasobek cisla Prvocislo while Nasobek <= Rozsah do begin ErastSito[Nasobek] := False; Nasobek := Nasobek + Prvocislo; end; repeat Prvocislo := Prvocislo + 1; // musim najit dalsi 100% prvocislo until (ErastSito[Prvocislo]) or (2 * Prvocislo > Rozsah); end;
Multiset // multiset_ctor.cpp // compile with: /EHsc #include #include int main( ) { using namespace std; multiset ::iterator ms1_Iter, ms2_Iter, ms3_Iter; multiset ::iterator ms4_Iter, ms5_Iter, ms6_Iter; // Create an empty multiset ms0 of key type integer multiset ms0; // Create an empty multiset ms1 with the key comparison // function of less than, then insert 4 elements multiset > ms1; ms1.insert( 10 ); ms1.insert( 20 ); ms1.insert( 20 ); ms1.insert( 40 ); // Create an empty multiset ms2 with the key comparison // function of geater than, then insert 2 elements multiset > ms2; ms2.insert( 10 ); ms2.insert( 20 ); // Create a multiset ms3 with the // allocator of multiset ms1 multiset ::allocator_type ms1_Alloc; ms1_Alloc = ms1.get_allocator( ); multiset ms3( less ( ), ms1_Alloc ); ms3.insert( 30 ); // Create a copy, multiset ms4, of multiset ms1 multiset ms4( ms1 ); // Create a multiset ms5 by copying the range ms1[_First, _Last) multiset ::const_iterator ms1_bcIter, ms1_ecIter; ms1_bcIter = ms1.begin( ); ms1_ecIter = ms1.begin( ); ms1_ecIter++; ms1_ecIter++; multiset ms5( ms1_bcIter, ms1_ecIter ); // Create a multiset ms6 by copying the range ms4[_First, _Last) // and with the allocator of multiset ms2 multiset ::allocator_type ms2_Alloc; ms2_Alloc = ms2.get_allocator( ); multiset ms6( ms4.begin( ), ++ms4.begin( ), less ( ), ms2_Alloc ); cout << "ms1 ="; for ( ms1_Iter = ms1.begin( ); ms1_Iter != ms1.end( ); ms1_Iter++ ) cout << " " << *ms1_Iter; cout << endl; cout << "ms2 = " << *ms2.begin( ) << " " << *++ms2.begin( ) << endl; cout << "ms3 ="; for ( ms3_Iter = ms3.begin( ); ms3_Iter != ms3.end( ); ms3_Iter++ ) cout << " " << *ms3_Iter; cout << endl; cout << "ms4 ="; for ( ms4_Iter = ms4.begin( ); ms4_Iter != ms4.end( ); ms4_Iter++ ) cout << " " << *ms4_Iter; cout << endl; cout << "ms5 ="; for ( ms5_Iter = ms5.begin( ); ms5_Iter != ms5.end( ); ms5_Iter++ ) cout << " " << *ms5_Iter; cout << endl; cout << "ms6 ="; for ( ms6_Iter = ms6.begin( ); ms6_Iter != ms6.end( ); ms6_Iter++ ) cout << " " << *ms6_Iter; cout << endl; } Output ms1 = ms2 = ms3 = 30 ms4 = ms5 = ms6 = 10 See Also
#include #include int main( ) { using namespace std; typedef pair Int_Pair; multimap ::iterator m1_Iter, m3_Iter, m4_Iter, m5_Iter, m6_Iter; multimap >::iterator m2_Iter; // Create an empty multimap m0 of key type integer multimap m0; // Create an empty multimap m1 with the key comparison // function of less than, then insert 4 elements multimap > m1; m1.insert( Int_Pair( 1, 10 ) ); m1.insert( Int_Pair( 2, 20 ) ); m1.insert( Int_Pair( 3, 30 ) ); m1.insert( Int_Pair( 4, 40 ) ); // Create an empty multimap m2 with the key comparison // function of geater than, then insert 2 elements multimap > m2; m2.insert( Int_Pair( 1, 10 ) ); m2.insert( Int_Pair( 2, 20 ) ); // Create a multimap m3 with the // allocator of multimap m1 multimap ::allocator_type m1_Alloc; m1_Alloc = m1.get_allocator( ); multimap m3( less ( ), m1_Alloc ); m3.insert( Int_Pair( 3, 30 ) ); // Create a copy, multimap m4, of multimap m1 multimap m4( m1 ); // Create a multimap m5 by copying the range m1[_First, _Last) multimap ::const_iterator m1_bcIter, m1_ecIter; m1_bcIter = m1.begin( ); m1_ecIter = m1.begin( ); m1_ecIter++; m1_ecIter++; multimap m5( m1_bcIter, m1_ecIter ); // Create a multimap m6 by copying the range m4[_First, _Last) // and with the allocator of multimap m2 multimap ::allocator_type m2_Alloc; m2_Alloc = m2.get_allocator( ); multimap m6(m4.begin( ), ++m4.begin( ), less ( ), m2_Alloc); cout second; cout second; cout second; cout second; cout second; cout second; cout << endl; } Output m1 = m2 = m3 = 30 m4 = m5 = m6 = 10 See Also
String #include #include using namespace std; int main(void) { string a = "Ahoj", b("svete"); string c,d; cout > d; cout << d; << endl; cout << "3. znak:" << c[2] << " z poctu:" << c.length() << endl; const char *stary = c.c_str(); cout << stary << endl; /* Nyní ukážu některé operátory */ if (a == b) { cout << "a == b" << endl; } if (a == string("Ahoj")) { cout << "a je Ahoj" << endl; a += string(" ctenari"); cout << a << endl; } return 0; }