Sobes.tech
Middle+

Podaj przykłady sytuacji, w których może wystąpić ponowne wyjątki w kontekście użycia konstrukcji 'podwójny wyjątek'.

sobes.tech AI

Odpowiedź od AI

Podwójne wyjątki w C++ występują, gdy podczas obsługi wyjątku (w bloku catch) pojawia się nowy wyjątek, zanim poprzednia obsługa zostanie zakończona. Prowadzi to do wywołania std::terminate.

Przykłady sytuacji:

  1. Wyrzucenie nowego wyjątku w bloku catch:

    #include <iostream>
    #include <stdexcept>
    
    void func() {
        throw std::runtime_error("Pierwszy wyjątek");
    }
    
    int main() {
        try {
            func();
        } catch (const std::runtime_error& e) {
            std::cerr << "Złapano: " << e.what() << std::endl;
            throw std::logic_error("Drugi wyjątek w catch"); // Nowy wyjątek
        }
        return 0;
    }
    

    Tutaj, w bloku catch, obsługującym std::runtime_error, wyrzucany jest nowy wyjątek std::logic_error.

  2. Wyjątek podczas czyszczenia zasobów w destruktorze w kontekście obsługi innego wyjątku:

    #include <iostream>
    #include <stdexcept>
    #include <vector>
    
    struct Resource {
        ~Resource() noexcept(false) { // Destruktor może wyrzucić wyjątek
            std::cerr << "Wywołano destruktor Resource" << std::endl;
            if (true) { // Warunek wywołujący wyjątek
                throw std::runtime_error("Wyjątek w destruktorze Resource");
            }
        }
    };
    
    void risky_func() {
        Resource r; // Tworzy obiekt, którego destruktor może wyrzucić wyjątek
        throw std::runtime_error("Wyjątek z risky_func"); // Pierwszy wyjątek
    }
    
    int main() {
        try {
            risky_func();
        } catch (const std::runtime_error& e) {
            std::cerr << "Złapano: " << e.what() << std::endl;
            // Tu może wystąpić wyjątek w destruktorze 'r' podczas obsługi pierwszego wyjątku
        }
        return 0;
    }
    

    Gdy risky_func wyrzuca wyjątek, następuje rozwinięcie stosu. W trakcie tego procesu wywoływany jest destruktor obiektu r. Jeśli destruktor ten wyrzuci wyjątek podczas obsługi pierwszego, pojawi się podwójny wyjątek.

  3. Wyjątek w obsłudze wyjątku z powodu błędów wewnętrznych lub wywołań funkcji wyrzucających wyjątki:

    #include <iostream>
    #include <stdexcept>
    #include <vector>
    
    void process_error(const std::exception& e) {
        std::cerr << "Przetwarzanie błędu: " << e.what() << std::endl;
        // Załóżmy, że ta funkcja może wyrzucić wyjątek w określonych warunkach
        if (e.what() && std::string(e.what()).length() > 20) {
             throw std::logic_error("Błąd podczas obsługi zbyt długiego komunikatu"); // Drugi wyjątek
        }
    }
    
    int main() {
        try {
            throw std::runtime_error("To dość długi komunikat o błędzie"); // Pierwszy wyjątek
        } catch (const std::exception& e) {
            process_error(e); // Ta funkcja może wyrzucić drugi wyjątek
        }
        return 0;
    }
    

    W tym przypadku funkcja process_error, wywoływana w bloku catch do obsługi pierwszego wyjątku, sama inicjuje drugi wyjątek.

We wszystkich tych scenariuszach, ponieważ drugi wyjątek pojawia się przed zakończeniem obsługi pierwszego (lub podczas rozwijania stosu z powodu pierwszego), standard C++ wymaga wywołania std::terminate. std::terminate domyślnie wywołuje abort().