Programování v jazyce C++ Pokročilé vlastnosti C++
Třída string je definována v hlavičkovém souboru string, slouží pro reprezentaci řetězců, jsou pro ni přetíženy následující operátory: +, +=, =, <<, >>, ==, <, >, <=, >=, != instanci typu string lze přiřadit jinou instanci typu string, znakové pole nebo znak, pro přístup k jednotlivým symbolům lze využít operátor [] nebo funkci at(),
Metody třídy string size(), length(), clear(), empty(), begin(), end(), insert(), replace(), erase(), swap(), copy(), append(), find_first_of, find_last_of, substr(), compare().
Šablony v C++ slouží k popisu množiny funkcí nebo datových typů se stejným názvem, ale různými typy parametrů, není třeba vytvářet pro každý datový typ novou funkci či datový typ, stačí vytvořit šablonu, daná funkce či datový typ je vytvořen(a) automaticky podle této šablony, lze vytvářet pro standardní i nově vytvořené datové typy.
Příprava šablony funkcí deklarace template<parametry> deklarace; příklad: template<class T> Max(T a, T b) { return a<b?b:a; } parametry mohou být typové, hodnotové nebo šablonové.
Typové parametry šablon specifikují se pomocí klíčového slova class, v tomto případě class neznamená, že jde nutně o objekt, novější překladače zavádějí klíčové slovo typename, takto specifikovaný typ lze použít v deklaraci funkce v místě, kde je očekáván konkrétní datový typ.
Další parametry šablon hodnotové vystupují v těle jako konstanty a musí být některého z těchto typů: celočíselný nebo výčtový, pointer nebo reference na objekt, pointer nebo reference na funkci, třídní pointer, šablonové říkají, že parametrem je šablona datového typu.
Použití šablony šablony se volají jako konkrétní funkce: c = Max(3, 9); překladač vytvoří potřebnou funkci při překladu a zakomponuje ji do výsledného kompilátu, není-li jednoznačné, jaká funkce se má vytvořit, překladač zahlásí chybu. překladači explicitně můžeme říct, jakou funkci má vytvořit.
Šablony objektových typů slouží k vytváření objektových datových typů se společnými vlastnostmi, které se liší jen v detailech, lze použít např. na zásobník, kde se mění typ ukládané hodnoty, takto vytvořený typ je rovnocenný jiným, lze z něho např. i dědit, instance datového typu vygenerovaného šablonou může být součástí jiné šablony, musejí se ale shodovat parametry.
Příprava šablony typu deklarace příklad: template<parametry> deklarace; příklad: template<class T> class prvek { ... }
Definice složek šablony každá metoda definovaná v šabloně je de facto šablonou, parametry šablony metody se musí shodovat s parametry šablony třídy, stejně je třeba definovat konstruktory i destruktor, statické atributy musí být definovány taktéž pomocí šablony.
Použití šablony typu instanci typu vytvořeného pomocí šablony můžeme deklarovat takto: zasobnik<int> a; instancí šablony je konkrétní datový typ, definice metod v šabloně ještě neznamená, že budou všechny vytvořeny, překladač může kód optimalizovat a vytvořit pouze volané metody, vždy se generují virtuální metody a všechny metody se generují při explicitním generování instance.
STL Standard Template Library, obsahuje šablony datových kontejnerů, iterátorů a algoritmů, kontejnery: umožňují ukládat a číst velká množství dat podle přesně zadaných pravidel, mezi kontejnery patří pole, seznamy, zásobníky, fronty, stromy, apod. iterátory: usnadňují zpracování dat z kontejnerů.
Společné vlastnosti kontejnerů všechny kontejnery v STL mají tyto společné metody: begin(), end(), size(), empty(), swap(), ostatní metody má každý kontejner jiné, všechny kontejnery lze procházet pomocí iterátorů.
Příklady kontejnerů vector, map, multimap, set, multiset, list, queue, stack, bitset, …
Výjimky výjimky slouží k ošetření chybových stavů, potenciálně nebezpečný blok se označí, v případě, že došlo k chybovému stavu, je vyvolána výjimka, tuto výjimku je možné zachytit handlerem a vyvolat ji později, úkolem výjimky je tento stav ošetřit a umožnit korektní pokračování programu.
Co se děje, nastane-li výjimka při vzniku výjimky se ukončí provádění dalších příkazů, řízení přebírá handler, který má tuto výjimku zachytit, program pak pokračuje po provedení handleru, handler se hledá i v nadřazených blocích, při opouštění bloku jsou volány destruktory, neexistuje-li handler, je program ukončen.
Používání výjimek v C++ potenciálně nebezpečný kód vnoříme do tzv. try bloku, handler se uvozuje klíčovým slovem catch, každý handler má parametr udávající, jakou výjimku má zachytit, jeden try blok může mít více handlerů, v případě potřeby výjimku vyvoláme pomocí příkazu throw.
Příklad použití výjimky try{ nebezpečný kód; } catch (výjimka1){ ošetření výjimky1 catch (výjimka2){ ošetření výjimky2
Objektová realizace výjimek jako výjimka v C++ může být vyvolána instance libovolného typu, je však slušné řešit výjimky pomocí objektových typů, jednou možností je vybudovat si vlastní hierarchii výjimek, je též možné využít hierarchii standardních výjimek.
Výběr handleru pro odchycení handler může odchytit výjimku typu daného v příkazu catch nebo typu odvozeného, je-li jako typ výjimky specifikován void*, je odchycen ukazatel na libovolný typ, jiné konverze nejsou uplatňovány, protože některé standardní výjimky obsahují i datové položky, je lepší výjimky předávat formou pointeru nebo reference.
Funkce vyvolávající výjimku pokud volaná funkce má propagovat podchycenou výjimku do funkce volající, je nutno toto deklarovat v hlavičce funkce, za hlavičkou funkce následuje příkaz throw a za ním v závorce seznam možných typů výjimek, výjimky, které nejsou v deklaraci specifikovány, se nesmí z funkce propagovat dále.
Částečné ošetření výjimek v jedné části je možné ošetřit pouze část problémů a výjimku poté propagovat do nadřazených bloků, toto se nazývá částečným ošetřením výjimky, výjimka se dále propaguje pomocí příkazu throw, v tomto případě se použije příkaz throw bez parametrů.
Standardní výjimky v C++ v C++ existuje mnoho standardních výjimek, základní třídou výjimek je třída exception, standardní výjimky jsou deklarovány v souboru stdexcept, logic_error a runtime_error a jejich potomci obsahují konstruktor s jedním textovým parametrem, s jehož pomocí lze výjimce předat hlášku popisující problém.
Hierarchie standardních výjimek exception: logic_error: length_error, domain_error, out_of_range, invalid_argument , bad_alloc, bad_exception,
Hierarchie standardních výjimek II. exception: bad_cast, bad_typeid, runtime_error: range_error, overflow_error, underflow_error.
Dynamická identifikace typů Run-Time Type Identification, protože předek může být zastoupen potomkem, nemusíme vždy vědět, kterého typu je instance, s níž pracujeme, RTTI nám umožní o tom získat přehled, můžeme využít tří nástrojů: operátor typeid, třída type_info, operátor dynamic_cast.
Operátor type_id deklarován v hlavičkovém souboru typeinfo, parametrem může být výraz nebo datový typ, vytváří instanci třídy type_info, druhý případ slouží pouze pro porovnání, je-li to možné, vyhodnotí se v době překladu, jinak až v době běhu, v případě nekorektního vstupu vyvolá výjimku bad_typeid.
Třída type_info deklarována v hlavičkovém souboru typeinfo, instance uchovává informace o typu daného výrazu, má následující položky: ==, !=, name(), before(), pro zjištění, zda je výraz daného typu, je nutné vytvořit instanci pro výraz i pro typ a ty pak porovnat na rovnost či nerovnost.
Operátor dynamic_cast použití: dynamic_cast<Typ> vyraz; slouží k: přetypování pointeru na objektový typ na pointer na jiný objektový typ v téže hierarchii, přetypování reference na objektový typ na referenci na jiný objektový typ v téže hierarchii, přetypování pointeru na objektový typ na void*.
Použití operátoru dynamic_cast funguje pouze pro polymorfní typy, dokonce je možné přetypování na virtuálního potomka (případně na pointer na virtuálního potomka), při nesprávném přetypování: vrátí NULL při přetypování pointerů , vyvolá výjimku bad_cast při přetypování referencí,
Operátor static_cast slouží k běžným převodům v C++, zprostředkovává též volání přetíženého operátoru přetypování, neumožňuje konverzi pointeru nebo reference na pointer nebo referenci na virtuálního potomka, umožňuje přetypování na různé objektové typy v rámci dané hierarchie, ovšem bez dynamické kontroly typů.
Další operátory přetypování const_cast: původní a nový typ se mohou lišit pouze modifikátory const a volatile, reinterpret_cast: zajišťuje nepřenositelné konverze, je možné převádět adresy na čísla apod., překladač nemá možnost žádné kontroly, nebezpečné!
Struktura složitějších programů delší zdrojové kódy je vhodné rozdělit do více souborů: soubory s příponou .c, soubory s příponou .h, tyto soubory lze překládat: vše dohromady, každý zvlášť, podle toho se vytvoří daný počet .o souborů.
Proč překládat soubory odděleně? umožní nám program rozdělit do menších přehlednějších souborů, ušetří se na administraci při překladu (např. tabulka identifikátorů), při změně v jednom ze zdrojových souborů není třeba překládat ostatní, při překladu v jednom balíku nelze mezi soubory skrýt globální identifikátory,
Obsah souboru .h definice maker a symbolických konstant použitelných i v jiných modulech, definice jednoduchých datových typů (pomocí typedef), deklarace objektových datových typů, deklarace globálních proměnných a globálních funkcí připravených pro použití v jiných modulech.
Obsah souboru .c nahrání potřebných hlavičkových souborů, deklarace importovaných objektů, definice globálních proměnných, definice lokálních maker, typů a proměnných úplné prototypy lokálních funkcí, definice globálních funkcí, definice lokálních funkcí.
Provázání jednotlivých souborů soubor .c si nahraje odpovídající hlavičkový soubor s implementací, o nahrání jiných modulů si říká taktéž soubor s příponou .c, hlavičkový soubor neobsahuje žádný příkaz #include, při využívání vytvořeného modulu je nahrán hlavičkový soubor.
Řízení platnosti identifikátorů globální proměnné a funkce platí vždy v rámci jednoho souboru, platnost lze rozšířit i do jiných souborů, k tomu slouží klíčové slovo extern, proměnná se definuje v jednom souboru a v dalších se zpřístupní deklarací s klíčovým slovem extern, omezit viditelnost globálních prvků pouze na daný soubor je možné pomocí paměťové třídy static.
Jak soubory překládat? pokud soubor s funkcí main() nahraje ostatní soubory a pak se to přeloží najednou, vznikne jeden .o soubor, pro překlad modulu je třeba překladači přidat parametr –c: gcc –c modul.c, výsledné .o soubory lze slinkovat do jednoho výsledného balíku.
Příkaz make usnadňuje překlad složitějších projektů, v souboru Makefile je možné si definovat pravidla pro překlad jednotlivých modulů a jejich názvy pak předávat příkazu make, v Makefilu lze definovat závislosti mezi jednotlivými moduly, příkaz make není svázán pouze s C/C++, lze jej používat pro překlad čehokoliv,
Struktura Makefilu první řádek pravidla obsahuje: název modulu, dvojtečku, nepovinný seznam modulů, na nichž tento závisí, další řádky pravidla obsahují: tabulátor, akci, počet akcí na jedno pravidlo není omezen.
Vyhodnocení pravidel pro překlad make bez parametrů spouští první pravidlo, make s názvem pravidla vyhledá pravidlo s tímto názvem, v obou případech vyhodnotí závislosti a nejprve vyhledá pravidla, na nichž je vybrané závislé, je-li cílový soubor novější než zdrojový, pravidlo se neprovede.
Příkaz make existuje více verzí programu, které se mohou od sebe v detailech lišit, vybrané parametry: -f Makefile – nastaví jméno Makefilu, -d – debuggovací mód, -s – tichý mód, -t – nevykoná překlad, pouze aktualizuje časy.
Použitá literatura Pavel Herout – Učebnice jazyka C, Miroslav Virius – Od C k C++, Slajdy na předmět X36PJC z akademického roku 2008/2009 (Ladislav Vágner, Karel Müller), FEL ČVUT, Slajdy na předmět 36PJC z akademického roku 2004/2005 (Petr Matyáš), FEL ČVUT, Server www.builder.cz.