Narzędzia użytkownika

Narzędzia witryny


opis_wyjatkow_w_bibliotece_standardowej

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), <stdlib.h>

Funkcja ta kończy program zamykając wszystkie pliki i opróżniając wszystkie bufory.

void abort(), <stdlib.h>

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 <exception> (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 <iostream>

PRZYKŁAD

/*
 *            Weronika Trybek I1ISI
 *    Zad. 5 Opis wyjątków w bibliotece standardowej.
 */
 
#include <iostream>
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

Weronika Trybek 2008/12/11 22:50

opis_wyjatkow_w_bibliotece_standardowej.txt · ostatnio zmienione: 2008/12/11 23:57 przez verkaufo