====== Opis wyjątków w bibliotece standardowej. ====== Obsługa wyjątków zapewnia reakcję na nietypowe zdarzenia w programie, a w szczególności sytuacje błędne. Przekazuje ona sterowanie do specjalnych procedur obsługi sytuacji wyjątkowych zwanych handlerami. Podstawowymi słowami kluczowymi dotyczącymi zajmowania się wyjątkami są: try, throw, catch ===== try ===== W bloku ''**try**'' znajdują się instrukcje, które mogą rzucać wyjątki. Kiedy nastąpi sytuacja wyjątkowa w jednej z instrukcji znajdujących się w tym bloku, sterowanie zostaje przekazane do odpowiedniego bloku ''**catch**''. Za blokiem ''**try**'' musi pojawić się co najmniej jeden blok ''**catch**'', w przeciwnym wypadku następuje błąd kompilacji. ===== throw ===== Służy do rzucania obiektów w sytuacji wyjątkowej. Działanie funkcji, w której nastąpi instrukcja ''**throw**'', zostaje natychmiast przerwane, a rzucony obiekt zostaje natychmiast przekazany do bloku ''**catch**'' jako argument, gdzie zostaje obsłużona sytuacja wyjątkowa. ===== catch ===== Blok ten łapie obiekty konkretnego typu rzucone w bloku ''**try**''. Jeśli typ rzuconego obiektu zgadza się z typem argumentu ''**catch**'' następuje obsługa sytuacji wyjątkowej przez ten jeden blok ''**catch**'' Kolejność bloków ''**catch**'' ma znaczenie, ponieważ kompilator wybiera PIERWSZY blok, który może złapać rzucony wyjątek, nawet jeśli kolejne bloki ''**catch**'' pasują lepiej. Każdy z bloków ''**catch**'' znajdujący się pod jednym blokiem ''**try**'' musi przyjmować inny argument. ''**catch(…)**'' - może łapać obiekty wszystkich typów. try { throw 1; } catch(int i) { cout << "Wyjątek! Nr " << i << endl; } ===== nie ma powrotu ===== Po rzuceniu wyjątku i obsłużeniu go w bloku ''**catch**'' nie następuje powrót do uprzednio wykonywanej funkcji, gdzie rzucono wyjątek za pomocą ''**throw**''. Wykonywane są instrukcje znajdujące się po sekwencji bloków ''**try**'' i ''**catch**''. ===== re-throw ===== W bloku ''**catch**'' możliwe jest rzucenie obiektu ‘dalej’, do dalszej obsługi za pomocą ''**throw;**'' bez argumentów. ===== deklaracja typów wyjątków rzucanych przez funkcję ===== Podczas deklaracji funkcji można ograniczyć typy wyjątków, które rzuca dana funkcja poprzez dodanie ''**throw (typ)**'': int funkcja(int i) throw (int); //funkcja rzuca wyjątki typu int int funkcja(int i) throw' (); //funkcja nie może rzucać żadnych wyjątków int funkcja(int i); //funkcja rzuca wyjątki dowolnego typu ===== odwikłanie stosu ===== Jeśli w bloku ''**try**'' powstały obiekty automatyczne, to w momencie, gdy wyjątek jest rzucany i sterowanie przechodzi do bloku ''**catch**'', następuje odwikłanie stosu. Jest to usunięcie wszystkich obiektów automatycznych, które zdarzyły powstać w bloku ''**try**'' w kolejności odwrotnej do tworzenia. try { Object a; Object b; Object c; throw 1; Object d; } // odwikłanie stosu: uruchomienie destruktora klasy Object, najpierw dla obiektu c, potem b, na końcu a, // nie zostanie on uruchomiony dla obiektu d, bo nie został on jeszcze utworzony. catch (int i) { cout << "Wyjątek! Nr " << i << endl; } ===== void exit(int status), ===== Funkcja ta kończy program zamykając wszystkie pliki i opróżniając wszystkie bufory. ===== void abort(), ===== Funkcja brutalnie kończy program, ‘nie sprzątając’ po sobie. ===== void terminate() ===== Gdy żaden z bloków ''**catch**'' nie potrafi obsłużyć wyjątku, wywoływana jest funkcja ''**terminate()**''. Funkcja ta zostanie również wywołana przy odwikłaniu stosu, gdy destruktor obiektu rzuca nowy wyjątek lub gdy pomiędzy rzuceniem obiektu, a złapaniem go w bloku ''**catch**'' nastąpi wywołanie konstruktora kopiującego, który próbuje rzucić nowym wyjątkiem. ''**terminate()**'' wywołuje funkcję ''**abort()**'', aby zmienić to wywołanie na funkcję wybraną przez programistę można to zrobić za pomocą funkcji ''**(void(*wsk)()) set_terminate(void (*wsk)())**''. ===== void unexpected() ===== Gdy funkcja deklaruje konkretne typy rzucanych wyjątków, a rzuca wyjątek innego typu, wyjątek ten nie może zostać obsłużony. Wywoływana jest wtedy funkcja ''**unexpected()**'', która domyślnie na koniec wywołuje funkcję ''**terminate()**''. Funkcją ''**(void (*wsk)()) set_unexpected(void (*wsk)())**'' można zmienić wykonywanie ''**terminate()**'', na rzecz wykonania funkcji wybranej przez programistę. ===== standardowe wyjątki ===== Biblioteka standardowa dostarcza klasę bazową dla obiektów, które są rzucane jako wyjątki. Zdefiniowana jest ona w ''****'' (przestrzeń nazw - ''**std**''). ^ __wyjątek__ ^ __opis__ ^ |bad_alloc | rzucany przez ''**new**'' przy nieudanej alokacji pamięci | |bad_cast | rzucany przez ''**dynamic_cast**'' | |bad_exception | rzucany gdy wyjątek nie pasuje do żadnego bloku ''**catch**'' | |bad_typeid | rzucany przez ''**typeid**'' | |ios_base::failure | rzucany przez funkcje z biblioteki ''****'' | ====== PRZYKŁAD ====== /* * Weronika Trybek I1ISI * Zad. 5 Opis wyjątków w bibliotece standardowej. */ #include using namespace std; /* * Klasa Sensor oraz klasa SmokeDetector po niej dziedzicząca, * to proste klasy, posiadające konstruktory i destruktory wyświetlające tekst * w celu łatwiejszego zobrazowania mechanizmu obsługi wyjątków. */ class Sensor { protected: int number_; char* text_; bool alarm_; public: Sensor() : number_(0), text_("Zwykly Czujnik"), alarm_(false) { cout << " Konstruktor domyslny Czujnika " << number_ << endl; } Sensor(int n, char* t) : number_(n), text_(t), alarm_(false) { cout << " Konstruktor Czujnika, "; } Sensor(const Sensor& s) { number_ = s.number_ + 1; text_ = s.text_; alarm_ = s.alarm_; cout << " Konstruktor kopiujacy Czujnika " << s.number_; if( text_ == "Zwykly Czujnik" ) cout << ", powstaje " << number_ << endl; else cout << ", "; } virtual ~Sensor() { cout << " Destruktor Czujnika " << number_ << endl; } virtual void alarmOn() { alarm_ = true; throw *this; } void alarmOff() { alarm_ = false; } }; class SmokeDetector : public Sensor { static int counter_; public: SmokeDetector() : Sensor(++counter_, "Czujnik Dymu") { cout << "Czujnika Dymu " << number_ << endl; } SmokeDetector(const SmokeDetector& m) : Sensor(m) { ++counter_; number_ = counter_; cout << "Czujnika Dymu, powstaje " << number_ << endl; } ~SmokeDetector() { --counter_; cout << " Destruktor Czujnika Dymu " << number_; } void alarmOn() { alarm_ = true; throw *this; } }; int SmokeDetector::counter_ = 0; int main() { cout << "\n\n***Obsluga wyjatku, bez referencji do obiektu w argumencie catch***\n\n"; try { Sensor sensor; // wywołanie konstruktora domyślnego Sensor (0) sensor.alarmOn(); // throw sensor; -> wywołanie konstruktora kopiującego Sensor (1) cout << "Jestem tutaj!\n"; // tekst ten nigdy nie będzie wyświetlony, bo sterowanie przechodzi do bloku catch } // sporządzenie kopii statycznej dla catch -> wywołanie konstruktora kopiującego Sensor (2) // odwikłanie stosu -> wywołanie destruktora Sensor (0) catch(Sensor s) { cout << "catch(Sensor s)\n"; } // koniec bloku catch -> wywołanie destruktora Sensor (2) i (1) cout << "\n\n***Obsluga wyjatku, z referencja do obiektu w argumencie catch***\n\n"; try { Sensor sensor; // wywołanie konstruktora domyślnego Sensor (0) sensor.alarmOn(); // throw sensor; -> wywołanie konstruktora kopiującego Sensor (1) } // odwikłanie stosu -> wywołanie destruktora Sensor (0) catch(Sensor& s) // wybrany catch { cout << "catch(Sensor& s)\n"; } // koniec bloku catch -> wywołanie destruktora Sensor (1) catch(...) // nie wchodzi tu juz, bo wyjątek został obsłuzony przez wczesniejszy catch { cout << "catch(...)\n"; } cout << "\n\n***Obsluga wyjatku, dziedziczenie oraz zagniezdzone bloki try i catch***\n\n"; try { // zewnetrzny try try // wewnetrzny try { SmokeDetector smokeDet; // wywołanie konstruktora Sensor (1) oraz SmokeDetector (1) SmokeDetector smokeDet2; // wywołanie konstruktora Sensor (2) oraz SmokeDetector (2) smokeDet.alarmOn(); // throw SmokeDetector;-> wywołanie konstruktora kopiującego Sensor (3) i SmokeDetector (3) } // odwikłanie stosu -> wywołanie destruktorów SmokeDetector (2), Sensor (2) oraz SmokeDetector (1), Sensor (1) catch(Sensor& s) // wybrany catch { cout << "catch(Sensor& s)\n"; throw; // dalsze rzucenie tego samego obiektu } // koniec bloku catch -> nie wywołany jest destruktor SmokeDetector, bo obiekt rzucony dalej catch(SmokeDetector& s) // nie wchodzi tu juz, mimo iż dopasowanie byłoby lepsze, { // bo wyjatek zostal obsluzony przez wczesniejszy blok catch cout << "catch(MotionSensor& s)\n"; } } catch(SmokeDetector& s) // wybrany catch, bo wyjątek został rzucony ponownie { cout << "zewnetrzny catch(MotionSensor& s)\n"; } // koniec bloku catch -> wywołanie destruktora SmokeDetector, Sensor (2) cout << "\n\n***Obsluga wyjatku, odwiklanie stosu sprzata tylko to, co zostalo \n utworzone na stosie," << " nie usuwa obiektow utworzonych za pomoca new***\n\n"; try { Sensor* sensor = new Sensor; // wywołanie konstruktora Sensor (0) sensor->alarmOn(); // throw sensor; -> wywołanie konstruktora kopiującego Sensor nr 1 SmokeDetector smokeDet; // ten obiekt nigdy nie bedzie utworzony } // odwikłanie stosu -> wywołanie destruktora wskaznika na Sensor (0), ale sam Sensor (0) nie zostaje usuniety catch(Sensor& s) { cout << "catch(Sensor& s)\n"; } // koniec bloku catch -> wywołanie destruktora Sensor (1) catch(...) { cout << "catch(...)\n"; } return 0; } ===== Wynik działania programu: ===== ***Obsluga wyjatku, bez referencji do obiektu w argumencie catch*** Konstruktor domyslny Czujnika 0 Konstruktor kopiujacy Czujnika 0, powstaje 1 Konstruktor kopiujacy Czujnika 1, powstaje 2 Destruktor Czujnika 0 catch(Sensor s) Destruktor Czujnika 2 Destruktor Czujnika 1 ***Obsluga wyjatku, z referencja do obiektu w argumencie catch*** Konstruktor domyslny Czujnika 0 Konstruktor kopiujacy Czujnika 0, powstaje 1 Destruktor Czujnika 0 catch(Sensor& s) Destruktor Czujnika 1 ***Obsluga wyjatku, dziedziczenie oraz zagniezdzone bloki try i catch*** Konstruktor Czujnika, Czujnika Dymu 1 Konstruktor Czujnika, Czujnika Dymu 2 Konstruktor kopiujacy Czujnika 1, Czujnika Dymu, powstaje 3 Destruktor Czujnika Dymu 2 Destruktor Czujnika 2 Destruktor Czujnika Dymu 1 Destruktor Czujnika 1 catch(Sensor& s) zewnetrzny catch(MotionSensor& s) Destruktor Czujnika Dymu 3 Destruktor Czujnika 3 ***Obsluga wyjatku, odwiklanie stosu sprzata tylko to, co zostalo utworzone na stosie, nie usuwa obiektow utworzonych za pomoca new*** Konstruktor domyslny Czujnika 0 Konstruktor kopiujacy Czujnika 0, powstaje 1 catch(Sensor& s) Destruktor Czujnika 1 --- //[[verkaufo@gmail.com|Weronika Trybek]] 2008/12/11 22:50//