====== 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//