Sdílet prostřednictvím


Moderní osvědčené postupy jazyka C++ pro výjimky a zpracování chyb

Ve většině scénářů je upřednostňovaným způsobem hlášení a zpracování chyb logiky i chyb modulu runtime ve většině scénářů použití výjimek. Platí to zejména v případě, že zásobník může obsahovat několik volání funkce mezi funkcí, která zjistí chybu, a funkcí, která má kontext pro zpracování chyby. Výjimky poskytují formální, dobře definovaný způsob pro kód, který detekuje chyby a předává informace do zásobníku volání.

Použití výjimek pro výjimečný kód

Chyby programu jsou často rozdělené do dvou kategorií:

  • Chyby logiky způsobené programováním Například chyba indexu mimo rozsah.
  • Chyby za běhu, které jsou nad kontrolou programátora. Například chyba "Síťová služba není k dispozici".

V programování ve stylu jazyka C a modelu COM se zasílání zpráv o chybách spravuje buď vrácením hodnoty, která představuje kód chyby nebo stavový kód konkrétní funkce, nebo nastavením globální proměnné, kterou volající může volitelně načíst po každém volání funkce, aby se zjistilo, jestli byly nahlášeny chyby. Například programování modelu COM používá vrácenou HRESULT hodnotu ke komunikaci chyb volajícímu. Rozhraní API Win32 má GetLastError funkci pro načtení poslední chyby hlášené zásobníkem volání. V obou těchto případech je na volajícím, aby kód rozpoznal a odpovídajícím způsobem na něj reagoval. Pokud volající kód chyby explicitně nezpracuje, program se může chybově ukončí bez upozornění. Nebo může i nadále spouštět chybná data a vést k nesprávným výsledkům.

Výjimky jsou v moderním jazyce C++ upřednostňované z následujících důvodů:

  • Výjimka vynutí volání kódu rozpoznat chybový stav a zpracovat ho. Neošetřené výjimky zastaví provádění programu.
  • Výjimka přeskočí na bod v zásobníku volání, který dokáže chybu zpracovat. Přechodné funkce můžou umožnit šíření výjimky. Nemusí koordinovat s jinými vrstvami.
  • Mechanismus pro uvolnění zásobníku výjimek po vyvolání výjimky zničí všechny objekty v oboru podle dobře definovaných pravidel.
  • Výjimka umožňuje čisté oddělení mezi kódem, který zjistí chybu a kód, který chybu zpracovává.

Následující zjednodušený příklad ukazuje nezbytnou syntaxi pro vyvolání a zachytávání výjimek v jazyce C++:

#include <stdexcept>
#include <limits>
#include <iostream>

using namespace std;

void MyFunc(int 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 jazyce C++ se podobají výjimkám v jazycích, jako jsou C# a Java. try Pokud je v bloku vyvolána výjimka, je zachycena prvním přidruženým catch blokem, jehož typ odpovídá výjimce. Jinými slovy, provádění přeskočí z throw příkazu na catch příkaz. Pokud se nenajde žádný použitelný blok catch, std::terminate vyvolá se a program se ukončí. V jazyce C++ může být vyvolán jakýkoli typ; doporučujeme však vyvolat typ, který je odvozen přímo nebo nepřímo z std::exception. V předchozím příkladu je typ invalid_argumentvýjimky definován ve standardní knihovně v souboru hlavičky <stdexcept> . C++ neposkytuje nebo nevyžaduje finally blok, aby se zajistilo, že jsou všechny prostředky uvolněny, pokud dojde k výjimce. Získání prostředků je inicializace (RAII) idiom, která používá inteligentní ukazatele, poskytuje požadované funkce pro vyčištění prostředků. Další informace naleznete v tématu Postupy: Návrh pro bezpečnost výjimek. Informace o mechanismu odvíjení zásobníku C++ naleznete v tématu Výjimky a odvíjení zásobníku.

Základní pokyny

Robustní zpracování chyb je v libovolném programovacím jazyce náročné. I když výjimky poskytují několik funkcí, které podporují dobré zpracování chyb, nemůžou za vás dělat všechno. Pokud chcete zjistit výhody mechanismu výjimek, mějte při návrhu kódu na paměti výjimky.

  • Pomocí kontrolních výrazů zkontrolujte podmínky, které by měly být vždy pravdivé nebo vždy nepravdivé. Pomocí výjimek zkontrolujte chyby, ke kterým může dojít, například chyby při ověřování vstupu u parametrů veřejných funkcí. Další informace najdete v části Výjimky versus kontrolní výrazy .
  • Výjimky použijte, když je kód, který zpracovává chybu, oddělen od kódu, který zjistí chybu jedním nebo více voláními intervenující funkce. Zvažte, jestli místo toho použít kódy chyb ve smyčce kritické pro výkon, když je kód, který chybu zpracovává, úzce svázaný s kódem, který ho zjistí.
  • Pro každou funkci, která může vyvolat nebo rozšířit výjimku, uveďte jednu ze tří záruk výjimek: silnou záruku, základní záruku nebo záruku nothrow (noexcept). Další informace naleznete v tématu Postupy: Návrh pro bezpečnost výjimek.
  • Vyvolejte výjimky podle hodnoty, zachyťte je odkazem. Nezachyťte, co nemůžete zvládnout.
  • Nepoužívejte specifikace výjimek, které jsou zastaralé v jazyce C++11. Další informace naleznete v části Specifikace výjimek a noexcept oddíl.
  • Při použití používejte standardní typy výjimek knihovny. Odvozujte vlastní typy výjimek z exception hierarchie tříd .
  • Nepovolujte výjimky, které uniknou z destruktorů nebo funkcí uvolnění paměti.

Výjimky a výkon

Mechanismus výjimky má minimální náklady na výkon, pokud není vyvolán žádná výjimka. Pokud dojde k vyvolání výjimky, náklady na procházení zásobníku a odvíjení jsou zhruba srovnatelné s náklady na volání funkce. Další datové struktury jsou potřeba ke sledování zásobníku volání po try zadání bloku a další pokyny jsou potřeba k uvolnění zásobníku, pokud dojde k výjimce. Ve většině scénářů ale náklady na výkon a využití paměti nejsou významné. Nepříznivý účinek výjimek na výkon je pravděpodobně významný pouze u systémů omezených pamětí. Nebo ve smyčce kritické pro výkon, kde se chyba pravděpodobně vyskytuje pravidelně, a mezi kódem, který ho hlásí, existuje úzká spojka mezi kódem, který ho zpracovává. V každém případě není možné znát skutečné náklady na výjimky bez profilace a měření. I v těch výjimečných případech, kdy jsou náklady významné, můžete je zvážit proti zvýšené správnosti, snadnější údržbě a dalším výhodám, které poskytují dobře navržené zásady výjimek.

Výjimky versus kontrolní výrazy

Výjimky a kontrolní výrazy jsou dva různé mechanismy pro detekci chyb za běhu v programu. Příkazy použijte assert k testování podmínek během vývoje, které by měly být vždy pravdivé nebo vždy nepravdivé, pokud je veškerý kód správný. Při zpracování takové chyby pomocí výjimky neexistuje žádný bod, protože chyba značí, že něco v kódu musí být opraveno. Nepředstavuje podmínku, kterou musí program obnovit za běhu. Zastaví assert provádění příkazu, abyste mohli zkontrolovat stav programu v ladicím programu. Výjimka pokračuje ve spouštění z první vhodné obslužné rutiny catch. Pomocí výjimek zkontrolujte chybové stavy, ke kterým může docházet za běhu, i když je váš kód správný, například "soubor nebyl nalezen" nebo "nedostatek paměti". Výjimky můžou tyto podmínky zpracovat, i když obnovení jenom vypíše zprávu do protokolu a program ukončí. Vždy zkontrolujte argumenty pro veřejné funkce pomocí výjimek. I když je vaše funkce bez chyb, možná nemáte úplnou kontrolu nad argumenty, které mu uživatel může předat.

Výjimky jazyka C++ versus výjimky WINDOWS SEH

Programy C i C++ můžou používat mechanismus strukturovaného zpracování výjimek (SEH) v operačním systému Windows. Koncepty v SEH se podobají těm ve výjimkách jazyka C++, s tím rozdílem, že SEH používá __trymísto try__except__finally a .catch V kompilátoru Microsoft C++ (MSVC) se pro SEH implementují výjimky jazyka C++. Při psaní kódu jazyka C++ však použijte syntaxi výjimek jazyka C++.

Další informace o SEH naleznete v tématu Strukturované zpracování výjimek (C/C++).

Specifikace výjimek a noexcept

Specifikace výjimek byly zavedeny v jazyce 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é ve standardu konceptu C++11. Doporučujeme nepoužívat throw specifikace výjimek s výjimkou throw(), což značí, že funkce neumožňuje žádné výjimky ukazovat. Pokud je nutné použít specifikace výjimek zastaralého formuláře throw( type-name ), podpora MSVC je omezená. Další informace naleznete v tématu Specifikace výjimek (throw). Specifikátor noexcept je představen v jazyce C++11 jako upřednostňovaná alternativa .throw()

Viz také

Postupy: Rozhraní mezi výjimečným a jiným kódem než výjimečným
Referenční dokumentace jazyka C++
Standardní knihovna C++