Ošetření chyb a výjimek (moderní verze jazyka C++)
V moderním C++ je ve většině případů použití výjimky upřednostňovaný způsob hlášení a zpracování logických chyb a chyb za běhu.To platí zejména když zásobník může obsahovat několik volání funkce mezi funkcí, která zjistí chyby a funkcí, která má kontext k určení, jak ji zpracovat.Výjimky představují formální, dobře definovaný způsob, jak má kód, který zjistí chyby, předat tuto informací výše v zásobníku volání.
Chyby programu lze obecně rozdělit do dvou kategorií: chyby logiky, které jsou způsobeny chybou při programování, například chyba „index mimo rozsah“, a chyby vzniklé za běhu, které jsou mimo kontrolu programátora, například chyba „nejsou k dispozici síťové služby“.Ve stylu programování C a modelu COM je hlášení chyb řešeno vrácením hodnoty, která představuje kód chyby nebo stavovým kódem pro určitou funkci nebo nastavením globální proměnné, kterou může volající volitelně získat po každém volání funkce pro zjištění, zda byly hlášeny chyby.Například programování v modelu COM používá k předání chyby volajícímu návratovou hodnotu HRESULT a rozhraní API systému Win32 má funkci GetLastError k načtení poslední chyby, která byla ohlášena v zásobníku volání.V obou těchto případech je na volajícím kód rozpoznat a reagovat na něj.Pokud volající nezpracuje explicitně kód chyby, může program spadnout bez varování nebo pokračovat v provádění chybných data a poskytovat nesprávné výsledky.
V moderním jazyce C++ jsou preferovány výjimky z následujících důvodů:
Výjimka vynutí, aby volající kód rozpoznal chybovou podmínku a zpracoval ji.Neošetřené výjimky zastaví spuštění programu.
Výjimka přejde k bodu v zásobníku volání, který může chybu zpracovat.Dílčí funkce mohou umožnit přenesení výjimky.Není nutné provádět koordinaci s jinými vrstvami.
Mechanismus uvolnění zásobníku výjimek odstraní po vyvolání výjimky všechny objekty v oboru podle přesně definovaných pravidel.
Výjimka umožňuje čisté oddělení mezi kódem, který rozpozná chybu, a kódem, který chybu zpracovává.
Následující zjednodušený příklad zobrazuje syntaxi potřebnou pro vyvolávání a záznam výjimek v jazyce C++.
#include <stdexcept>
#include <limits>
#include <iostream>
using namespace std;
class MyClass
{
public:
void MyFunc(char c)
{
if(c < numeric_limits<char>::max())
throw invalid_argument("MyFunc argument too large.");
//...
}
};
int main()
{
try
{
MyFunc(256); //cause an exception to throw
}
catch(invalid_argument& e)
{
cerr << e.what() << endl;
return -1;
}
//...
return 0;
}
Výjimky v C++ se podobají těm v jazycích jako je C# nebo Java.Jestliže u bloku try vznikne výjimka vyvolána, bude zachycena prvním přiřazeným blokem catch, jehož typ odpovídá typu výjimky.Jinými slovy spuštění přejde z příkazu throw na příkaz catch.Pokud není nalezen žádný použitelný blok catch, std::terminate je vyvolána a program skončí.V jazyce C++ může být vyvolán libovolný typ; doporučujeme však, abyste vyvolali typ odvozený přímo nebo nepřímo z std::exception.V předchozím příkladu, je typ výjimky invalid_argument definován ve standardní knihovně v souboru hlaviček <stdexcept>.Jazyk C++ neposkytuje a nevyžaduje blok finally k zajištění, že jsou všechny prostředky uvolněny, pokud je vyvolána výjimka.Získávání prostředků je idiom inicializace (RAII), který používá inteligentní ukazatele, poskytuje požadované funkce vyčištění prostředků.Další informace naleznete v tématu Postupy: Návrh s ohledem na bezpečnost výjimek.Informace o mechanismu uvolnění zásobníku C++ naleznete v tématu Výjimky a unwinding zásobníku v jazyce C++.
Základní pokyny
Robustní zpracování chyby je obtížné v každém programovacím jazyce.Přestože výjimky poskytují několik funkcí, které podporují dobré zpracování chyb, nemohou všechnu práci udělat za vás.Abyste poznali výhody mechanismu výjimek, mějte na paměti výjimky při návrhu vašeho kódu.
Použití nepodmíněných výrazů ke kontrole chyb, které by nikdy neměly vzniknout.Výjimky použijte ke kontrole chyb, které mohou nastat, například chyby při ověřování vstupních parametrů veřejných funkcí.Další informace naleznete v tématu v části nazvané Výjimky a kontrolní výrazy.
Použijte výjimky, pokud kód, který zpracovává chybu, může být oddělen od kódu, který rozpozná chybu při volání jedné nebo více zasahujících funkcí.Zvažte, zda radši nepoužít kódy chyb ve smyčkách výkonově kritického kódu, když je kód, který zpracovává chyby, těsně spjat s kódem, který je rozpoznává.Další informace o tom, kdy nepoužívat výjimky, naleznete v tématu When Not to Use Exceptions.
Každá funkce, která může vyvolat nebo propagovat výjimku, poskytuje jednu ze tří záruk výjimky: silnou záruku, základní záruku nebo záruku nothrow (noexcept).Další informace naleznete v tématu Postupy: Návrh s ohledem na bezpečnost výjimek.
Vyvolání výjimky podle hodnoty, jejich zachycení podle reference.Nezachycujte, co nemůžete zpracovat.Další informace naleznete v tématu Pokyny pro vyvolání a zachycení výjimek (C++).
Nepoužívejte specifikace výjimek, které jsou zastaralé v C++ 11.Další informace naleznete v tématu v části nazvané Specifikace výjimek a noexcept.
Používejte standardní typy výjimek knihovny, pokud jsou uplatněny.Odvozujte vlastní typy výjimek z hierarchie tříd výjimek.Další informace naleznete v tématu Jak: použít standardní výjimku objekty knihovny.
Nepovolovat výjimkám unikat z destruktorů nebo z funkcí navracení paměti.
Výjimky a výkon
Mechanismus výjimek má minimální vliv zatížení, pokud nedojde k žádné výjimce.Pokud je vyvolána výjimka, náklady na průchod zásobníku a unwinding jsou zhruba srovnatelné s náklady na volání funkce.Další datové struktury jsou požadovány ke sledování zásobníku volání po zadání bloku try a další pokyny jsou nezbytné k provedení operace unwind na zásobníku v případě vyvolání výjimky.Nicméně ve většině případů náklady na výkon a paměť není důležité.Nepříznivý vliv výjimek na výkon bude významný pouze v případě systému s velmi omezenou pamětí, nebo u smyčky nutné k zajištění výkonu, kde k chybám může docházet pravidelně a kód pro vyřešení situace je těsně spjat s kódem, který podává hlášení.V žádném případě není možné zjistit skutečné náklady výjimek bez profilování a měření.I ve vzácných případech, kdy jsou náklady významné, je můžete vyvážit zvýšenou správností, jednodušší udržovatelností a dalšími výhodami poskytovanými dobře navrženým systémem výjimek.
Výjimky vs. kontrolní výrazy
Výjimky a kontrolní výrazy jsou dva odlišné mechanismy pro detekci chyb za běhu programu.Použijte nepodmíněné výrazy pro testování podmínek během vývoje, který by měl mít nikdy hodnotu true, pokud je váš kód správně.Nemá žádný smysl zpracovávat takové chyby pomocí výjimky, protože chyby označují, že něco v kódu má být opraveno a nereprezentuje podmínku, která má program obnovit v době běhu.Metoda Assert zastaví provedení u příkazu, takže můžete zkontrolovat stav programu v ladicím programu; výjimka pokračuje v provádění od první vhodné obslužné rutiny zachycení.Pomocí výjimky chybové stavů, které mohou nastat za běhu i v případě, že váš kód je správný, například "soubor nebyl nalezen" nebo "nedostatek paměti". Můžete chtít obnovit z těchto podmínek, i v případě obnovení pouze výstupů zprávy do protokolu a ukončení programu.Vždy kontrolujte argumenty pro veřejné funkce pomocí výjimek.I v případě, že je vaše funkce bez chyb, nemusíte mít úplnou kontrolu nad argumenty, které jí předává uživatel.
Výjimky v C++ vs. výjimky Windows SEH
Programy v jazyce C i C++ mohou využívat mechanismus strukturovaného zpracování výjimek (SEH) v operačním systému Windows.Základní pojmy v SEH jsou podobné výjimkám v jazyce C++, až na to, že SEH používá konstrukty __try, __except a __finally namísto výrazů try a catch.V jazyce Visual C++ jsou výjimky C++ implementovány pro SEH.Při psaní kódu jazyka C++ však používejte synax výjimek C++.
Další informace o knihovnách SEH naleznete v tématu Strukturované zpracování výjimek (C/C++).
Specifikace výjimek a noexcept
Specifikace výjimek byly zavedeny v C++ jako způsob, jak určit výjimky, které může funkce vyvolat.Specifikace výjimek se však v praxi ukázaly jako problematické a jsou zastaralé v návrhu normy C ++ 11.Doporučujeme vám nepoužívat specifikace výjimek s výjimkou throw(), která označuje, že výjimka nepovoluje žádné únikové výjimky.Pokud je nutné použít specifikace výjimek typu throw(type), uvědomte si, že Visual C++ se v některých ohledech liší od standardu.Další informace naleznete v tématu Specifikace výjimek.Specifikátor noexcept je představen v C ++ 11 upřednostňovaná alternativa k throw().
Viz také
Koncepty
Postupy: Rozhraní mezi kódem výjimek a ostatním kódem
Další zdroje
C++ vás vítá zpět (moderní verze jazyka C++)