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:
-
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ącymstd::runtime_error, wyrzucany jest nowy wyjątekstd::logic_error. -
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_funcwyrzuca wyjątek, następuje rozwinięcie stosu. W trakcie tego procesu wywoływany jest destruktor obiektur. Jeśli destruktor ten wyrzuci wyjątek podczas obsługi pierwszego, pojawi się podwójny wyjątek. -
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 blokucatchdo 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().