Zaawansowane programowanie w C++ (PCP) Wykład 5 - obsługa błędów. Mechanizm wyjatków. dr inż. Robert Nowak - p. 1/24
Powtórzenie - wzorce wzorce kreacyjne fabryka abstrakcyjna (abstract factory) prototyp (prototype) singleton (singleton) wzorce strukturalne adapter (wrapper) fasada (fasade) most (bridge) proxy (proxy) kompozyt (composite) wzorce czynnościowe iterator (iterator) polecenie (command) odwiedzajacy (visitor) obserwator (observer) interpreter (interpreter) strategia (strategy) stan (state) - p. 2/24
Wzorzec wizytatora ustalona hierarchia klas wiele metod, metody moga się zmieniać Wizytator + wizytuj(elementa*) + wizytuj(elementb*) + wizytuj(elementc*) Hierarchia klas Element + accept(visitor) KonkretnyWizytator1 KonkretnyWizytator2 ElementA ElementB ElementC + wizytuj(elementa*) + wizytuj(elementb*) + wizytuj(elementc*) + wizytuj(elementa*) + wizytuj(elementb*) + wizytuj(elementc*) + accept(visitor) + accept(visitor) + accept(visitor) - p. 3/24
Wizytator (2) class Visitor;//Deklaracja class Element { //Klasa bazowa virtual void accept(visitor& v) = 0; ; /* Deklaracje konkretnych elementów */ class Visitor { //Abstrakcyjny wizytator public: virtual void visit(elementa&) = 0; virtual void visit(elementb&) = 0; virtual void visit(elementc&) = 0; ; class ElementA : public Element { public: virtual void accept(visitator& v) { v.visit(*this);//woła metodę visit(elementa&) //Dalsza część implementacji klasy ElementA ; - p. 4/24
»» kod powrotu» zmienna globalna» błędny stan obiektu autor klasy (biblioteki) może wykryć bład, lecz nie wie, co z nim zrobić użytkownik klasy (biblioteki) wie co zrobić z błędem, lecz nie potrafi go wykryć Mechanizmy: Nieprawidłowe: ignorowanie błędów kończenie działania programu komunikaty dla użytkownika. Kłopotliwe: kod powrotu zmienna globalna: ostatni bład specjalny błędny stan obiektu. Mechanizm wyjatków. - p. 5/24
kod powrotu»» kod powrotu» zmienna globalna» błędny stan obiektu Metoda zwraca wartość, która jest interpretowana jako powodzenie (lub niepowodzenie) pewnej operacji. class Wektor { //... bool add(int i);//zwraca false gdy niepowodzenie ; //Przykład użycia Foo foo; if(!foo.add(4)) //Sprawdzanie, czy nie wystąpił błąd //Tutaj kod obsługi błędu Mechanizm niewygodny ponieważ: rezerwacja zwracanej wartości na kod błędu każda funkcja musi być badana, czy nie sygnalizuje wystapienia błędu programista może zignorować fakt wystapienia błędu kod obsługi błędów wymieszany z innym kodem - p. 6/24
zmienna globalna»» kod powrotu» zmienna globalna» błędny stan obiektu Funkcja, która sygnalizuje wystapienie błędu ustawia odpowiednia wartość zmiennej globalnej. Mechanizm typowy dla C (#include <errno.h>) extern int errno;//zmienna globalna, przechowuje kody błędów class Plik { //... zapisz(const char* buf);//ustawia errno gdy wystąpi błąd ; Plik p;//przykład użycia p.zapisz(bufora); if(errno == ENOFILE)//Sprawdzanie, czy wystąpił określony błąd //Obsługa błędu obiekt globalny - potencjalne źródło kłopotów należy co jakiś czas sprawdzać wartość programista może zignorować fakt wystapienia błędu kod obsługi błędów wymieszany z innym kodem - p. 7/24
błędny stan obiektu»» kod powrotu» zmienna globalna» błędny stan obiektu Przykład: class Macierz { public: //Tworzy macierz o podanych rozmiarach explicit Macierz(int sizex=2, int sizey=2); Macierz operator+(const Macierz& m) const { if(m.sizex()!= sizex() m.sizey()!= sizey() ) return Macierz(-1,-1);//błędna macierz //... Wady: nie zawsze jest możliwe wprowadzenie wartości błędnej programista może zignorować fakt wystapienia błędu - p. 8/24
Mechanizm wyjatków» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane class Wektor { int* tab; int rozm; public: class Zakres{ ; //Klasa wyjątku int& operator[](int index); ; int& Wektor::operator[](int i) { if(i >= 0 && i < rozm) return tab[i]; else throw Zakres(); //Funkcja biblioteczna rzuca wyjątek void f(wektor& w) { try { //Funkcja użytkownika będzie przechwytywać wyjątki g(w); catch(wektor::zakres) { //Procedura obsługi wyjątku //kod obsługi błędu - p. 9/24
w konstruktorach» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane class Wektor { static const int MAX = 32000; //Składowe takie jak poprzednio public: class Rozmiar { ; //klasa wyjątku złego rozmiaru Wektor(int r); ; Wektor::Wektor(int r) { if(r < 0 r >= MAX) throw Rozmiar();//Zgłaszanie błędu w //utworzenie obiektu void f() { try { //używaj wektorów catch(wektor::zakres) { /* obsługa błędu zakresu */ catch(wektor::rozmiar) { /* obsługa błędu rozmiaru */ - p. 10/24
Składnia» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane blok-try: try instrukcja-złożona lista-proc-obsługi lista-proc-obsługi: proc-obsługi [lista-proc-obsługi] proc-obsługi: catch ( deklaracja-wyjatku ) instrukcja-złożona deklaracja-wyjatku: lista-specyfikatorów-typu deklarator lista-specyfikatorów-typu... wyrażenie-zgłoszenia: throw [wyrażenie] specyfikacja-wyj atków: throw [ ( lista-typów ) ] - p. 11/24
Klasy, które reprezentuja» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane Nie używaj typów standardowych do sygnalizowania błędów Powinny dziedziczyć po std::exception, posiadaja funkcje wirtualne Tworzone podczas zgłaszania wyjatku w specjalnym miejscu Konstruktor nie może zgłaszać wyjatków! Dostarcza konstruktora kopiujacego rzucanie przez wartość, przechwytywane przez referencję //Przykładowa klasa błędu przy 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 getbadchar() const return bad_char; - p. 12/24
Grupowanie wyjatków» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane class ExceptionMat : public std::exception { ; class ExceptionDzielZero : public ExceptionMat { //Składowe i metody specyficzne dla tego błędu ; class ExceptionNadmiar : public ExceptionMat { //Składowe i metody specyficzne dla tego błędu ; Procedura obsługi dla wyjatku wywołuje się gdy: typ użyty do wyłapywania wyjatku jest identyczny jak typ rzuconego wyjatku typ użyty do wyłapywania jest klasa podstawowa - p. 13/24
Rzucanie wyjatku» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane przez wartość (obiekt jest tworzony w specjalnym miejscu) przez wskaźnik - problemy z zarzadzaniem pamięcia int my_atoi(const string& str) { for(int i=0;i<str.size();++i) { char curr = str[i]; if(!isdigit(curr) ) throw InputException(curr); /* implementacja */; ; - p. 14/24
Obsługa wyjatku» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane należy łapać przez referencje (unika się kopiowania) Istotna jest kolejność sekcji catch try { /* Kod, który może wyrzucić wyjątek */ catch(const std::ios_base::failure&) { /* błąd strumienia */ catch(const std::exception&) { /* dowolny wyjątek std */ Odrzucenie wyjatku try { //Kod, który może zgłosić wyjątek catch(const ExceptionMat&) { //Zrób, co się da throw; //zgłoś ponownie wyjątek - p. 15/24
Standardowe klasy wyjatków» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane exception bad_alloc bad_cast bad_typeid bad_exception ios_base::failure logic_error runtime_error out_of_range invalid_argument overflow_error Nazwa bad_alloc bad_cast bad_typeid bad_exception zgłaszajacy new dynamic_cast typeid throw biblioteki standardowej (st Nazwa kto zgłasza out_of_range invalid_argument overflow_error ios_base::failure at() bitset bitset clear() - p. 16/24
mechanizmów wyjatków a» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane inny mechanizm powrotu korzysta ze zwijania stosu void f1() { int* tmp = new int; //Kod, który używa tmp i może rzucić wyjątkiem delete tmp; //Nieudolna poprawa void f2() { int* tmp = new int; try { //Kod, który używa tmp i może rzucić wyjątkiem catch(...) { //Przechwytuje wszystkie wyjątki delete tmp; throw; delete tmp; - p. 17/24
Zdobywanie zasobów jest» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane class WskaznikDoInt { int* ptr; public: WskaznikDoInt(int* p) : ptr(p) { WskaznikDoInt() { delete ptr; operator int*() { return ptr; int* get() { return ptr; void f3() { WskaznikDoInt pi(new int); (*tmp) = 4;//przykładowy kod wykorzystujący obiekt pi //Kod, który używa wskaźnika może rzucić wyjątkiem //destruktor obiektu lokalnego pi wywoła operator delete - p. 18/24
Zdobywanie zasobów jest» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane class WskaznikDoPliku { FILE* p; public: WskaznikDoPliku(const char* nazwa, const char* atr) { p = fopen(nazwa, atr); WskaznikDoPliku() { fclose(p); operator FILE*() { return p; - p. 19/24
niewyłapane» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane Funkcja terminate() domyślna implementacja: exit(1) zmiana zachowania domyślnego: typedef void(*pfv)(); PFV set_terminate(pfv); przykład: void bladkrytyczny(){ ErrorLog::getInstance().errorMsg( nie wyłapany wyjątek ) exit(1); int main() { set_terminate(&bladkrytyczny); catch(...) - przechwytuje wszystkie wyjatki. w destruktorach - p. 20/24
Specyfikacja interfejsu» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane Można określić jakie wyjatki zgłasza że funkcja (metoda) void f() throw (ExceptionMat); funkcja (metoda) która nie zgłasza wyjatków: void f() throw (); jeżeli nie ma sekcji throw w deklaracji funkcji przyjmuje się, że może ona zgłaszać dowolny wyjatek wyjatek bad_exception metoda unexpected() - domyślnie woła terminate() zmiana zachowania domyślnego typedef void(*pfv)(); PFV set_unexpected(pfv); - p. 21/24
Podsumowanie» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane Rodzaje klas: klasy autonomiczne klasy bazowe dla hierarchii klas klasy wyjatków biblioteka nie powinna przerywać działanie programu biblioteka nie powinna drukować komunikatu o błędzie nie każda funkcja musi obsługiwać każdy możliwy bład funkcja main powinna obsługiwać wszystkie wyjatki stosuj zasadę zdobywanie zasobów jest używaj wyjatków do obsługi błędów! - p. 22/24
boost::scoped_ptr» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane Odpowiada dokładnie strategii zdobywanie zasobów jest Destruktor usuwa wskaźnik Zabronione kopiowanie template<typename T> class scoped_ptr : noncopyable { public: explicit scoped_ptr(t* p = 0); scoped_ptr();//usuwa obiekt wskazywany void reset(t* p = 0);//Zmienia wskaźnik T& operator*() const; T* operator->() const; T* get() const; ; void f() { scoped_ptr<mojaklasa> klasa(new MojaKlasa); //kod, który może wyrzucać wyjątki //Destruktor klasy auto_ptr wywoła operator delete - p. 23/24
Wzorzec pimpl» w konstruktorach» Klasy, które reprezentuja» mechanizmów wyjatków a» niewyłapane Ukrywanie implementacji: class Foo { public: //Interfejs klasy private: struct Impl;//Deklaracja poprzedzająca scoped_ptr<impl> pimpl_;//sprytny wskaźnik ; - p. 24/24