Programovanie (1) v C/C++
1-INF-127, ZS 2024/25
Letný semester, prednáška č. 4: Rozdiel medzi revíziami
Riadok 7: | Riadok 7: | ||
== Výnimky == | == Výnimky == | ||
+ | Mechanizmus ''výnimiek'' (angl. ''exceptions'') slúži v Jave na spracovanie chýb a iných výnimočných udalostí, ktoré môžu počas vykonávania programu nastať. Doposiaľ sme v našich programoch takéto situácie viac-menej ignorovali – napríklad sme obvykle predpokladali, že vstup vždy spĺňa požadované podmienky, že súbor, z ktorého sa pokúšame čítať, vždy existuje, atď. Dôvodom bola predovšetkým prílišná prácnosť ošetrovania chýb pomocou podmienok a neprehľadnosť programov, ktoré takéto podmienky obsahujú. | ||
− | === | + | Uvažujme napríklad nasledujúci jednoduchý program, ktorý zo vstupu načíta prirodzené číslo <tt>n</tt> nasledované <tt>n</tt> celými číslami, ktoré postupne poukladá do poľa dĺžky <tt>n</tt>. Následne číta zo vstupu postupnosť indexov v rozmedzí <tt>0</tt> až <tt>n-1</tt> a po každom načítaní indexu <tt>i</tt> zvýši hodnotu <tt>a[i]</tt> o jedna. Načítavanie vstupu sa ukončí po načítaní reťazca <tt>"KONIEC"</tt>. |
+ | <syntaxhighlight lang="java"> | ||
+ | public static void main(String[] args) { | ||
+ | Scanner scanner = new Scanner(System.in); | ||
+ | int n = scanner.nextInt(); | ||
+ | int a[] = new int[n]; | ||
+ | for (int i = 0; i <= n - 1; i++) { | ||
+ | a[i] += scanner.nextInt(); | ||
+ | } | ||
+ | String next = scanner.next(); | ||
+ | while (!next.equals("KONIEC")) { | ||
+ | a[Integer.parseInt(next)]++; | ||
+ | next = scanner.next(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | Vykonávanie tohto programu môže skončiť chybou viacerými rôznymi spôsobmi: používateľ napríklad môže namiesto niektorého čísla zadať nečíselný reťazec; číslo <tt>n</tt> môže ďalej zadať ako záporné, čím vznikne chyba pri pokuse o alokovanie poľa; niektorý z ním zadaných indexov do poľa tiež nemusí byť v požadovanom rozmedzí. Po pokuse o ošetrenie týchto chýb pomocou podmienok dostávame nasledujúci horibilný program. | ||
+ | <syntaxhighlight lang="java"> | ||
+ | /** Nasledujuci kod je ukazkou toho, ako sa osetrovanie chyb v Jave NEMA robit. */ | ||
+ | |||
+ | /** Metoda, ktora zisti, ci je retazec nebielych znakov reprezentaciou celeho cisla. */ | ||
+ | private static boolean isInteger(String s) { | ||
+ | Scanner stringScanner = new Scanner(s); | ||
+ | return stringScanner.hasNextInt(); | ||
+ | } | ||
+ | |||
+ | public static void main(String[] args) { | ||
+ | Scanner scanner = new Scanner(System.in); | ||
+ | int n = 0; | ||
+ | if (scanner.hasNextInt()) { | ||
+ | n = scanner.nextInt(); | ||
+ | if (n < 0) { | ||
+ | System.out.println("Pocet prvkov pola nemoze byt zaporny."); | ||
+ | System.exit(2); | ||
+ | } | ||
+ | } else { | ||
+ | System.out.println("Vstup sa nezacina cislom."); | ||
+ | System.exit(1); | ||
+ | } | ||
+ | int a[] = new int[n]; | ||
+ | for (int i = 0; i <= n - 1; i++) { | ||
+ | if (scanner.hasNextInt()) { | ||
+ | a[i] += scanner.nextInt(); | ||
+ | } else { | ||
+ | System.out.println("Niektory z prvkov pola nebol zadany ako cele cislo."); | ||
+ | System.exit(3); | ||
+ | } | ||
+ | } | ||
+ | String next = scanner.next(); | ||
+ | while (!next.equals("KONIEC")) { | ||
+ | if (isInteger(next)) { | ||
+ | int i = Integer.parseInt(next); | ||
+ | if (i >= 0 && i <= n - 1) { | ||
+ | a[i]++; | ||
+ | } else { | ||
+ | System.out.println("Niektory z indexov do pola je mimo povoleneho rozmedzia."); | ||
+ | System.exit(5); | ||
+ | } | ||
+ | } else { | ||
+ | System.out.println("Niektory z indexov do pola nebol zadany ako cele cislo."); | ||
+ | System.exit(4); | ||
+ | } | ||
+ | next = scanner.next(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | Vidíme, že už aj pri takto jednoduchej úlohe dostávame pomerne rozsiahly program, v ktorom väčšinu kódu zaberá práve spracovanie chýb. Ešte nepríjemnejšou je však skutočnosť, že ošetrovanie chýb je potrebné implementovať v mieste programu, kde táto chyba vznikla. To vedie k prelínaniu podstatných častí programu s časťami, ktoré slúžia iba na spracovanie chýb, v dôsledku čoho sa kód stáva veľmi neprehľadným. Z uvedených dôvodov sme až doposiaľ podobné chybové udalosti väčšinou ignorovali. | ||
+ | |||
+ | V praxi je ale nutné podobné chyby náležite poošetrovať – nie je totiž napríklad prípustné, aby textový editor spadol zakaždým, keď sa jeho používateľ pokúsi otvoriť súbor s neexistujúcim názvom. Výnimky poskytujú spôsob, ako spracovanie chybových udalostí realizovať podstatne efektívnejším spôsobom, než v ukážke vyššie. Medzi ich základné prednosti totiž patria: | ||
+ | * Možnosť oddelenia kódu na spracovanie chýb od zvyšku kódu. | ||
+ | * Možnosť jednoduchým spôsobom ponechať spracovanie chyby na volajúcu metódu v prípade, že sa to javí ako vhodnejšie riešenie. | ||
+ | * Možnosť využívať pri spracúvaní chýb prostriedky objektovo orientovaného programovania. | ||
=== Mechanizmus výnimiek v Jave === | === Mechanizmus výnimiek v Jave === |
Verzia zo dňa a času 15:21, 4. marec 2021
Obsah
Oznamy
- Počas najbližších cvičení, čiže v stredu 10. marca od 11:30 do 13:00 bude prebiehať prvý test. Bude pozostávať z troch úloh zameraných na látku z prvých troch týždňov, ktorých riešenia bude potrebné odovzdávať na testovač. Po dobu riešenia testu je potrebná účasť na stretnutí „Cvičenia” v MS Teams.
- V stredu tiež budú zverejnené dve nebodované úlohy k štvrtým cvičeniam, zamerané na látku z dnešnej prednášky. Ďalšie podobne zamerané úlohy budú zverejnené na cvičeniach budúci týždeň.
- Prvú domácu úlohu je potrebné odovzdať najneskôr do začiatku budúcej prednášky, čiže do pondelka 15. marca, 9:00.
Výnimky
Mechanizmus výnimiek (angl. exceptions) slúži v Jave na spracovanie chýb a iných výnimočných udalostí, ktoré môžu počas vykonávania programu nastať. Doposiaľ sme v našich programoch takéto situácie viac-menej ignorovali – napríklad sme obvykle predpokladali, že vstup vždy spĺňa požadované podmienky, že súbor, z ktorého sa pokúšame čítať, vždy existuje, atď. Dôvodom bola predovšetkým prílišná prácnosť ošetrovania chýb pomocou podmienok a neprehľadnosť programov, ktoré takéto podmienky obsahujú.
Uvažujme napríklad nasledujúci jednoduchý program, ktorý zo vstupu načíta prirodzené číslo n nasledované n celými číslami, ktoré postupne poukladá do poľa dĺžky n. Následne číta zo vstupu postupnosť indexov v rozmedzí 0 až n-1 a po každom načítaní indexu i zvýši hodnotu a[i] o jedna. Načítavanie vstupu sa ukončí po načítaní reťazca "KONIEC".
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int a[] = new int[n];
for (int i = 0; i <= n - 1; i++) {
a[i] += scanner.nextInt();
}
String next = scanner.next();
while (!next.equals("KONIEC")) {
a[Integer.parseInt(next)]++;
next = scanner.next();
}
}
Vykonávanie tohto programu môže skončiť chybou viacerými rôznymi spôsobmi: používateľ napríklad môže namiesto niektorého čísla zadať nečíselný reťazec; číslo n môže ďalej zadať ako záporné, čím vznikne chyba pri pokuse o alokovanie poľa; niektorý z ním zadaných indexov do poľa tiež nemusí byť v požadovanom rozmedzí. Po pokuse o ošetrenie týchto chýb pomocou podmienok dostávame nasledujúci horibilný program.
/** Nasledujuci kod je ukazkou toho, ako sa osetrovanie chyb v Jave NEMA robit. */
/** Metoda, ktora zisti, ci je retazec nebielych znakov reprezentaciou celeho cisla. */
private static boolean isInteger(String s) {
Scanner stringScanner = new Scanner(s);
return stringScanner.hasNextInt();
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = 0;
if (scanner.hasNextInt()) {
n = scanner.nextInt();
if (n < 0) {
System.out.println("Pocet prvkov pola nemoze byt zaporny.");
System.exit(2);
}
} else {
System.out.println("Vstup sa nezacina cislom.");
System.exit(1);
}
int a[] = new int[n];
for (int i = 0; i <= n - 1; i++) {
if (scanner.hasNextInt()) {
a[i] += scanner.nextInt();
} else {
System.out.println("Niektory z prvkov pola nebol zadany ako cele cislo.");
System.exit(3);
}
}
String next = scanner.next();
while (!next.equals("KONIEC")) {
if (isInteger(next)) {
int i = Integer.parseInt(next);
if (i >= 0 && i <= n - 1) {
a[i]++;
} else {
System.out.println("Niektory z indexov do pola je mimo povoleneho rozmedzia.");
System.exit(5);
}
} else {
System.out.println("Niektory z indexov do pola nebol zadany ako cele cislo.");
System.exit(4);
}
next = scanner.next();
}
}
Vidíme, že už aj pri takto jednoduchej úlohe dostávame pomerne rozsiahly program, v ktorom väčšinu kódu zaberá práve spracovanie chýb. Ešte nepríjemnejšou je však skutočnosť, že ošetrovanie chýb je potrebné implementovať v mieste programu, kde táto chyba vznikla. To vedie k prelínaniu podstatných častí programu s časťami, ktoré slúžia iba na spracovanie chýb, v dôsledku čoho sa kód stáva veľmi neprehľadným. Z uvedených dôvodov sme až doposiaľ podobné chybové udalosti väčšinou ignorovali.
V praxi je ale nutné podobné chyby náležite poošetrovať – nie je totiž napríklad prípustné, aby textový editor spadol zakaždým, keď sa jeho používateľ pokúsi otvoriť súbor s neexistujúcim názvom. Výnimky poskytujú spôsob, ako spracovanie chybových udalostí realizovať podstatne efektívnejším spôsobom, než v ukážke vyššie. Medzi ich základné prednosti totiž patria:
- Možnosť oddelenia kódu na spracovanie chýb od zvyšku kódu.
- Možnosť jednoduchým spôsobom ponechať spracovanie chyby na volajúcu metódu v prípade, že sa to javí ako vhodnejšie riešenie.
- Možnosť využívať pri spracúvaní chýb prostriedky objektovo orientovaného programovania.