Mechanizmy obsługi błędów¶
Nieprawidłowe:
Ignorowanie błędów
Kończenie działania programu
Komunikaty dla użytkownika
Kłopotliwe:
Kod powrotu
Zmienna globalna: ostatni błąd
Specjalny błędny stan obiektu
ANSI: longjmp
Mechanizm wyjątków.
Kod powrotu¶
Metoda zwraca wartość, która jest interpretowana jako poprawne wykonanie funkcji lub jako błąd. Rezerwacja zwracanej wartości na kod błędu. Funkcja musi być badana, czy nie sygnalizuje wystąpienia błędu (użytkownik może zignorować fakt wystąpienia błędu). Kod obsługi błędów jest wymieszany z innym kodem.
class Container
{
/* … */
public:
bool push_back(int i); //zwraca false gdy niepowodzenie
};
Przykład użycia:
Container vec;
if ( !vec.push_back(4) ) { // sprawdzanie, czy nie wystąpił błąd
// tutaj kod obsługi błędu
}
vec.push_back(8); // ignorowanie potencjalnego błędu
Zmienna globalna¶
Sygnalizacja wystąpienia błędu przez ustawienie wartości zmiennej globalnej (np. #include <errno.h>
). Należy co jakiś czas sprawdzać wartość – użytkownik może zignorować fakt wystąpienia błędu. Kod obsługi błędów jest wymieszany z innym kodem.
extern int errno; //zmienna globalna, przechowuje kody błędów
class File {
/* … */
void save(const char* buffer); // ustawia errno, gdy wystąpi błąd
};
Przykład użycia:
File p;
p.save(text);
if ( errno == FILE_NOT_FOUND ) {// sprawdzanie, czy wystąpił określony błąd
// obsługa błędu
}
Obsługa wyjątków¶
Jeśli niespodziewana sytuacja pojawi się wewnątrz funkcji, fakt ten zostaje zakomunikowany użytkownikowi przy pomocy specjalnej instrukcji throw
. Program przechodzi z normalnego trybu wykonywania programu do trybu obsługi wyjątków. W trybie tym opuszczane są wszystkie wywołane dotąd funkcje lub bloki programu tak długo, aż zostanie napotkany blok obsługi danego wyjątku catch
– zwijanie stosu.
Wyjątki standardowe są obiektami zdefiniowanymi za pomocą klas dziedziczących po klasie std::exception
. Atrybuty wyjątku mogą być wykorzystane w celu uzyskania informacji opisującej sytuację wyjątkową. Każda klasa wyjątku reprezentuje jakiś jego konkretny typ.
Bloki try/catch¶
Obiektowe podejście do obsługi sytuacji wyjątkowych. Kod, który może generować błędy umieszczamy w bloku try
. Wyłapywanie wyjątków oraz obsługa konkretnego błędu umieszczona jest w osobnym bloku catch
.
try
{
File* source = new File("code.cpp");
source->open();
string content = source->read_to_end();
...
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
}
Wielokrotne bloki catch¶
Bloki catch
mogą wielokrotnie występować dla jednego bloku try
. Każdy blok catch
obsługuje inny rodzaj wyjątku. Blok try
może mieć jeden ogólny blok catch
.
try
{
// kod, który próbujemy wykonać
}
catch (const std::out_of_range& e) { /*...*/ }
catch (const std::exception& e) { /*...*/ }
catch (...) { /*...*/ }
Bloki catch¶
Bloki catch
przeszukiwane są zgodnie z porządkiem ich wystąpienia w programie. Wykonane zostają instrukcje tylko z pierwszego bloku, który odpowiada danemu typowi wyjątku.
Sekwencje bloków catch
należy tworzyć tak, aby bloki zawierające obsługę bardziej wyspecjalizowanych klas poprzedzały bloki dotyczące bardziej ogólnych klas wyjątków.
Wszystkie bloki programu na drodze od wykrycia wyjątku do jego obsługi zostają porzucone – zwijanie stosu – usuwane są wszystkie lokalne obiekty utworzone w tych blokach.
Mechanizm wyjątków¶
class Array
{
int* buffer_;
int size_;
public:
class IndexOutOfRange { }; // klasa wyjątku
int& operator[](int index);
};
int& Array::operator[](int i)
{
if(i >= 0 && i < size_)
return buffer_[i];
else
throw IndexOutOfRange("opis");
}
void do_something(Array& a)
{
a[1000] = 13; // operator[] może rzucić wyjątkiem Array::IndexOutOfRange
}
void f(Array& a)
{
try
{
do_something(a);
}
catch(const Array:: IndexOutOfRange& a)
{
// blok obsługi wyjątku
// kod obsługi błędu
}
}
Właściwości mechanizmu wyjątków:
Nie rezerwuje wartości zwracanej
Mniej argumentów wywołań funkcji
Konstruktory mogą zwracać błędy
Brak obiektów globalnych
Użytkownik nie może zignorować zgłoszonego błędu
Kod obsługi błędów oddzielony od innego kodu
Wymaga wsparcia przez język
Mechanizm rzucania wyjątku
Inny mechanizm powrotu
Korzysta ze zwijania stosu
Specyfikacja interfejsu
Niewyłapane wyjątki
Klasy reprezentujące błędy¶
Specjalny typ klas
Powinny dziedziczyć po
std::exception
Konstruktor wyjątku nie może zgłaszać wyjątków
Często implementują wzorzec wizytatora
Tworzone podczas zgłaszania wyjątku w specjalnym miejscu
Przykładowa klasa błędu podczas konwersji napisu na liczbę:
class InputException : public std::exception
{
char bad_char_;
public:
InputException(char c) : bad_char_(c) {}
InputException(const InputException& e) throw() : bad_char_(e.bad_char_)
{
}
char bad_char() const
{
return bad_char_;
};
};
Standardowe klasy wyjątków¶
exception |
Klasa bazowa wszystkich wyjątków wyrzucanych z biblioteki standardowej C++. Metoda what() zwraca const char* (łańcuch opisujący wyjątek). |
---|---|
logic_error |
Klasa pochodna klasy exception. Zgłasza błędy w warstwie logicznej programu, które prawdopodobnie mogą zostać wykryte przez uważne przejrzenie kodu. |
runtime_error |
Klasa pochodna klasy exception. Zgłasza błędy wykonania, które mogą być wykryte właściwie tylko podczas działania tego programu. |
domain_error |
Zgłasza naruszenie warunków wstępnych. invalid_argument Informuje, że do funkcji, z której wyrzucono ten wyjątek, przekazano błędny argument. |
length_error |
Informuje o próbie stworzenia obiektu, którego długość jest większa lub równa npos (największej wartości typu size_t, jaką można zapisać). |
out_of_range |
Zgłasza informację, że argument jest spoza zakresu. |
bad_cast |
Wyrzucany w przypadku wykonania nieprawidłowego rzutowania |
dynamic_cast |
podczas identyfikacji typu w czasie wykonania programu. |
bad_typeid |
W wyrażeniu typeid(*p)pojawił się pusty (równy nullptr) wskaźnik p. |
range_error |
Zgłasza naruszenie warunków końcowych. |
overflow_error |
Zgłasza przepełnienie w wyniku operacji arytmetycznych. |
bad_alloc |
Zgłasza błąd związany z alokacją pamięci. |
Nieobsłużone wyjątki¶
Nieobsłużony wyjątek powoduje wywołanie funkcji std::terminate()
, która z kolei wywołuje funkcję std::abort()
. Istnieje możliwość zaimplementowania alternatywnej funkcji zakończenia za pomocą funkcji std::set_terminate()
.
void myTerminate()
{
cout << “Uncaught exception!\n”;
exit(1);
}
void (*oldFunction)();
oldFunction = std::set_terminate(myTerminate);