Sobes.tech
Middle+

Uveďte príklady situácií, kedy môže dôjsť k opätovnému výnimke v kontexte použitia konštrukcie 'dvojitá výnimka'.

sobes.tech AI

Odpoveď od AI

Dvojitá výnimka (double exception) v C++ nastáva, keď počas spracovania výnimky (napríklad v bloku catch) vznikne nová výnimka skôr, než je ukončené spracovanie predchádzajúcej. To vedie k volaniu std::terminate.

Príklady takýchto situácií:

  1. Vyhodenie novej výnimky v bloku catch:

    #include <iostream>
    #include <stdexcept>
    
    void func() {
        throw std::runtime_error("Prvá výnimka");
    }
    
    int main() {
        try {
            func();
        } catch (const std::runtime_error& e) {
            std::cerr << "Chytené: " << e.what() << std::endl;
            throw std::logic_error("Druhá výnimka v catch"); // Nová výnimka
        }
        return 0;
    }
    

    Tu v bloku catch, ktorý spracováva std::runtime_error, je vyhodená nová std::logic_error.

  2. Výnimka počas čistenia zdrojov v destruktore počas spracovania inej výnimky:

    #include <iostream>
    #include <stdexcept>
    #include <vector>
    
    struct Resource {
        ~Resource() noexcept(false) { // Destruktor môže vyvolať výnimku
            std::cerr << "Volanie destruktora Resource" << std::endl;
            if (true) { // Podmienka, ktorá spôsobí výnimku
                throw std::runtime_error("Výnimka v destruktore Resource");
            }
        }
    };
    
    void risky_func() {
        Resource r; // Objekt, destruktor ktorého môže vyvolať výnimku
        throw std::runtime_error("Výnimka z risky_func"); // Prvá výnimka
    }
    
    int main() {
        try {
            risky_func();
        } catch (const std::runtime_error& e) {
            std::cerr << "Chytené: " << e.what() << std::endl;
            // Tu môže nastať výnimka v destruktore 'r', zatiaľ čo spracovávame prvú
        }
        return 0;
    }
    

    Keď risky_func vyhodí výnimku, dochádza k odvíjaniu zásobníka. Počas odvíjania je volaný destruktor objektu r. Ak tento destruktor sám vyvolá výnimku, zatiaľ čo spracovávame prvú výnimku, vznikne dvojitá výnimka.

  3. Výnimka v obsluhe výnimky kvôli interným chybám alebo volaniam funkcií, ktoré vyvolávajú výnimky:

    #include <iostream>
    #include <stdexcept>
    #include <vector>
    
    void process_error(const std::exception& e) {
        std::cerr << "Spracovanie chyby: " << e.what() << std::endl;
        if (e.what() && std::string(e.what()).length() > 20) {
             throw std::logic_error("Chyba pri spracovaní príliš dlhého hlásenia"); // Druhá výnimka
        }
    }
    
    int main() {
        try {
            throw std::runtime_error("Toto je dosť dlhé hlásenie o chybe"); // Prvá výnimka
        } catch (const std::exception& e) {
            process_error(e); // Tento volanie môže vyvolať druhé výnimka
        }
        return 0;
    }
    

    V tomto prípade funkcia process_error, ktorá je volaná v bloku catch na spracovanie prvej výnimky, sama spôsobí druhú výnimku.

Vo všetkých týchto scenároch, pretože druhá výnimka vznikne pred dokončením spracovania prvej (alebo počas odvíjania zásobníka kvôli prvej), štandard C++ vyžaduje volanie std::terminate. std::terminate vo výchozom nastavení volá abort().