vypracovani temat z 3. prednasky
This commit is contained in:
98
Výjimky.md
Normal file
98
Výjimky.md
Normal file
@@ -0,0 +1,98 @@
|
||||
Výjimka je způsob jak aktivně kontrolovat správný chod programu a popasovat se s nečekanými chybami. V podstatě mě zajímají 3 hlavní operátory a to:
|
||||
- **try** -> blok, ve kterém se snažím zachytit chybu
|
||||
- **catch** -> blok, který řeší co se stane, když nastane chyba
|
||||
- **throw** -> operátor, který umí chybu vyhodit
|
||||
|
||||
Dále se u základní C++ setkáme s třemi knihovnami, které s chybami souvisí a to?
|
||||
- `<exception>` - základní funkcionalita chyb
|
||||
- `<stdexcept>` - seznam typů chyb, které můžeme využít na "házení" při problémech
|
||||
- `<new>` - umí vyhazovat chyby typické u práci s pamětí (třeba nedostatek místa)
|
||||
|
||||
Funkcionalitu musíme přidat pomocí `#include <exceptions>`
|
||||
|
||||
Hlavní smysl chyb je zachytit nečekané věci v programu. Neměli bychom řešit problémy s kterými počítáme. Zatímco můžeme řešit třeba dělení nulou, které nestane při očekáváném vstupu od uživale (a nějaký typ chyby na to existuje), tak protože to čekáme a připravujeme se na to, tak bychom to měli řešit spíše jinak. Chyba má smysl hlavně v případech, které nelze odtušit a to například:
|
||||
- otevření souboru, který je prázdný
|
||||
- nedostatek paměti
|
||||
- přetečení
|
||||
Chyby jsou praktické, ale zatěžují program. Proto je třeba s nimi být opatrný a nepoužívat je na všechno zbytečně (a také samozřejmě kvůli přehlednosti).
|
||||
|
||||
Pro embedded systémy existuje alternativní způsob jak řešit chyby, který není tak náročný a to `std::optional` a `std::expected` v C++23.
|
||||
|
||||
# Základ
|
||||
|
||||
Strukturálně program s chytáním chyb vypadá:
|
||||
```cpp
|
||||
try
|
||||
{
|
||||
# zde je blok kde hledám chybu
|
||||
}
|
||||
catch
|
||||
{
|
||||
# zde je blok kde řeším chybu
|
||||
}
|
||||
```
|
||||
|
||||
## Try
|
||||
`try` funguje jednoduše, všechno co je uvnitř je zpracovaný výjimka a můžu řešit, co s ní pak udělám pomocí `catch`. S tím pak můžu řešit důmyslnou logiku výjimek, kdy na různých vrstvách programu řeším různé problémy dle toho, kdy se mi to hodí.
|
||||
Například na nízké vrstvě můžu vyhodit nějakou chybu ze špatné operace. na střední vrstvě se rozhodnu, jestli je řešitelná (můžu ji třeba napravit) a nebo jestli ne. Pokud ne, tak ji pošlu do vysoké vrstvy, která ji chytí a vypíše uživateli a ukončí proces, kde vznikla.
|
||||
|
||||
Všechny chyby, které nejsou chyceny pomocí `try` spustí `std::terminate`, což způsobí pád programu.
|
||||
|
||||
Tohle je vhodné si spojit ještě s konceptem **stack unwinding**. To je jak chyba cestuje -> stále nahoru v cestě programu (stack -> cesta volání - spíš paměť, kde je cesta uložena). Všechno putuje nahoru a když to skončí úplně nahoře v main, tak je průser.
|
||||
|
||||
## Throw
|
||||
`throw` vyhazuje chybu. Může to být text, např: `throw "Tady je chyba\n";` , ale obecně je spíš vhodné vyhodit nějaké oficiální objekt představující nějaký typ chyby z `<stdexcept>`.
|
||||
|
||||
např: ` throw std::invalid_argument( "received negative value" );`
|
||||
- zde je možné poslat společně s typem chyby argument se zprávou...
|
||||
|
||||
|
||||
## Catch
|
||||
V bloku `catch`, který následuje za `try` se řeší, jak se zachovat v případě nějaké chyby
|
||||
```cpp
|
||||
try
|
||||
{
|
||||
#něco
|
||||
}
|
||||
|
||||
#catch("Text") {} #nedává smysl, nechytá se hodnota, ale typ
|
||||
catch(const std::bad_alloc& e) {} #vyjímka špatné alokace
|
||||
catch(int) {} #chytí všechny integery
|
||||
catch(std::exception) {} #chytí všechny chyby
|
||||
catch(...) {}#chytí úplně všechno
|
||||
```
|
||||
|
||||
`Catch` funguje jako funkce. Má argumenty, které nejsou hodnoty, ale typy hodnot, které jdou "zavolat". Proto nemůžu chytat text, ale můžu chytat např. `const char* e` -> a `e` je to co se předá.
|
||||
|
||||
v případe `catch(const std::bad_alloc& e) {}` se používá [[Reference]] protože objekt může být teoreticky velký a my ho nechceme kopírovat.
|
||||
## What()
|
||||
`.what()` je funkce chybových objektů. Ta vypisuje jejich textovou hodnotu.
|
||||
Například pro `throw std::invalid_argument( "received negative value" );`
|
||||
vypíše `"received negative value"`.
|
||||
|
||||
Volal bych třeba
|
||||
```cpp
|
||||
catch(const std::bad_alloc& e)
|
||||
{
|
||||
std::cout << e.what()
|
||||
}
|
||||
```
|
||||
|
||||
## set_terminate a terminate()
|
||||
Když nastane situace, kdy se dojde k nějakému fatálnímu programu, na který program není připraven, zavolá se `std::terminate()`. Ten zavolá `abort()` a ukončí program.
|
||||
To může nastat, když výjimka není zachycena, ale případů je víc:
|
||||
- během stack unwindingu (tedy při opouštění funkcí po `throw`) dojde k další výjimce
|
||||
- vyhodí se výjimku z destruktoru během unwindingu
|
||||
- porušení pravidla `noexcept` (funkce označená jako `noexcept` vyhodí výjimku)
|
||||
|
||||
`terminate` je poslední funkce, která se zavolá před ukončením programu a nevrací dále dál tok programu.
|
||||
My pokud chceme můžeme nastavit vlastní funkci `terminate`, pro větší kontrolu nad programem pomocí `std::set_terminate(my_func);`
|
||||
|
||||
**Pozn**: destruktory nesmí házet výjimky proto, protože při výjimce nastane situace, kdy se ukončuje funkce a destruktory se volají. Pokud by destruktor vyhodil výjimku, mohla by nastat situace, kdy jsou 2 výjimky najednou, což je problém.
|
||||
# Typy chyb
|
||||
|
||||
- runtime chyby -> range, overflow, etc.
|
||||
- logické chyby -> domain, out_of_range...
|
||||
- bad_cast
|
||||
- bad_function_call
|
||||
- bad_alloc -> většinou nedostatek paměti od new...
|
||||
Reference in New Issue
Block a user