C++ wykład 7 (28.03.2012) Wyjątki.

Slides:



Advertisements
Podobne prezentacje
Wstęp do strumieni danych
Advertisements

>>MoJa SzKoŁa I Ja<<
C++ wykład 9 ( ) Szablony.
Rekurencja 1 Podprogram lub strukturę danych nazywamy rekurencyjną, (recursive subprogram, recursive data structure) jeżeli częściowo składa się z samej.
Mechanizmy pracy równoległej
Dzisiejszy wykład Wyjątki.
Standardowa biblioteka języka C++
1 Dzisiejszy wykład Wzorce funkcji Wzorce klas. 2 Wzorce Często pojawia się konieczność pisania podobnych funkcji lub klas operujących na argumentach.
1 Dzisiejszy wykład Wzorce funkcji Wzorce klas Tablica asocjacyjna Składowe statyczne.
Wprowadzenie.
Bezpieczeństwo wyjątków w C++: OpenGL
Semafory Autorzy : Michał Winciorek Łukasz Jackowicz.
Wydajne aplikacje na platformie .NET
Wybrane elementy języka Java
Szablony (wzorce) Przykład 1: Szablon klasy -
Licznik template<class Count_Type> class Count { public:
Obsługa błędów w programach i zapobieganie ich występowaniu.
Bartosz Walter Inżynieria oprogramowania Lecture XXX JavaTM – część II Bartosz Walter
Inżynieria oprogramowania Lecture XXX JavaTM – część IV: IO
Refaktoryzacja czyli odświeżanie kodu
Podstawy informatyki Powtórka Grupa: 1A Prowadzący: Grzegorz Smyk
Multimedia Obrazy i dźwięki w apletach Javy, praca z tekstem, zdarzenia od myszki, obsługa wyjątków w Javie, aplety z wątkami, animacja poklatkowa.
Wyrównanie spostrzeżeń pośrednich niejednakowo dokładnych
Przykład – sieć niwelacyjna
Rachunek Wyrównawczy Wyrównanie spostrzeżeń bezpośrednich
Wykład nr 7: Synchronizacja procesów
Systemy operacyjne Wykład nr 5: Wątki Piotr Bilski.
Wykład nr 2: Struktura systemu komputerowego a system operacyjny
C++ wykład 7 ( ) Wyjątki. Ogólne spojrzenie na wyjątki Wyjątki zaprojektowano do wspierania obsługi błędów. System wyjątków dotyczy zdarzeń synchronicznych.
Niepewności przypadkowe
Temat nr 10: System przerwań
Komunikaty sterujące zestawu protokołów TCP/IP
ZARZĄDZANIE PROCESAMI
Wyjątki, rzutowanie typów, operator instanceOf
Systemy Czasu Rzeczywistego
Podstawy programowania II
przykładowy 8-bitowy mikroprocesor uniwersalny CISC
Programowanie urządzeń mobilnych – wykład IV
Funkcje w Pascalu Przypomnienie wiadomości o procedurach Prowadzący: Anna Kaleta Piotr Chojnacki.
Java 3 MPDI Programowanie obiektowe W7. import java.io.*; public class X { // kontrukcja throws – określenie jakie wyjątki może dana metoda // sygnalizować
Podstawy programowania II
Informatyka I - Wykład ANSI C
PL/SQL Zajęcia nr II PL/SQL(2) M. Rakowski - WSISiZ.
STEROWANIE Ale nie tylko
Seminarium problemowe
Programowanie obiektowe Wykład 6 dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/14 Dariusz Wardowski.
Regulacja trójpołożeniowa
Warsztaty C# Część 6 Grzegorz Piotrowski Grupa .NET PO
Podstawy informatyki 2013/2014 Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi.
W ą t e k (lekki proces) thread.
K URS JĘZYKA C++ – WYKŁAD 7 ( ) Wyjątki.
Kurs języka C++ – wykład 4 ( )
Technologie internetowe Wykład 7 Kontrola danych użytkownika.
Instrukcje wyboru.
Metody matematyczne w inżynierii chemicznej
Wyjątki. Po co nam wyjątki? Często pisząc jakiś kod staramy się go uczynić uniwersalnym, jesteśmy w stanie wykryć sytuacje niepoprawne, ale nasz kod może.
Zestaw pytań nr. 3 Typy generyczne Wyjątki OPRACOWALI: JAKUB GRYCZEWSKIKINGA ROSA DANIEL KAPTEJNYWOJCIECH ŁĘCZYCKI
Temat 1 Pojęcie systemu operacyjnego Opracował: mgr Marek Kwiatkowski.
PO13-1 / 19 Wykład 13 Wyjątki i ich zgłaszanie Wyłapywanie wyjątków Obsługa wyjątków Wykorzystanie polimorfizmu Filtrowanie wyjątków Błędy w konstruktorach.
Podstawy informatyki Mechanizm obsługi sytuacji wyjątkowych Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu.
Strumienie w języku Java Bartosz Walter InMoST Wielkopolska sieć współpracy w zakresie innowacyjnych metod wytwarzania oprogramowania Termin realizacji:
Zdarzenia niepożądane
Programowanie Obiektowe – Wykład 6
Inżynieria oprogramowania
Dzisiejsze zajęcia będą wyjątkowe…
Wątki, programowanie współbieżne
(według:
Haskell Składnia funkcji.
Refaktoryzacja czyli odświeżanie kodu
Zapis prezentacji:

C++ wykład 7 (28.03.2012) Wyjątki

Ogólne spojrzenie na wyjątki Wyjątki zaprojektowano do wspierania obsługi błędów. System wyjątków dotyczy zdarzeń synchronicznych – na przykład do kontroli zakresu czy błędów we/wy. Mechanizm obsługi wyjątków można traktować jako nielokalną strukturę sterującą, która korzysta ze zwijania stosu.

Obsługa błędów Wyjątek to obiekt sygnalizujący błąd. Wyjątki można zgłaszać po wykryciu sytuacji krytycznej instrukcją throw. Wyjątki zgłoszone w bloku try można wyłapać w bloku catch i obsłużyć sytuację problemową. Wyjątki w sposób naturalny dzielą kod na część obliczeniową i część sterującą obliczeniami.

Zgłaszanie i łapanie wyjątków Wyjątek to obiekt dowolnego typu. Kod wykrywający błąd zgłasza wyjątek instrukcją throw, na przykład: throw 15; throw "problem z dokładnością obliczeń"; throw moja_klasa(7.532); Chęć złapania wyjątku sygnalizuje się umieszczeniem kodu w instrukcji try-catch. Wynikiem działania throw jest zwinięcie stosu, aż do znalezienia odpowiedniego bloku catch (w funkcji, która bezpośrednio lub pośrednio wywołała funkcję zgłaszająca wyjątek).

Zgłaszanie i łapanie wyjątków Przykład zgłoszenia i obsługi błędu: try { int x = 0; cerr << "integer (>0) "; cin >> x; if (!cin) throw "i/o error"; if (x<=0) throw x; // … } catch (const char *ex) { cerr << "number format error" << endl; } catch (int ex) { cerr << "number value error" << endl; }

Zgłaszanie i łapanie wyjątków Program może obsługiwać tylko wyjątki zgłaszane w bloku try. Po zgłoszeniu wyjątku, sterowanie nie wraca już do miejsca zgłoszenia. Po obsłużeniu wyjątku w bloku catch sterowanie przenoszone jest za instrukcję try-catch. Wyjątki są rozróżniane po typie.

Grupowanie wyjątków Wyjątki często w sposób naturalny tworzą rodziny (zastosowanie dziedziczenia w strukturalizacji wyjątków). Przykład hierarchii wyjątków: class BladMat {}; class Nadmiar : public BladMat {}; class Niedomiar : public BladMat {}; class DzielZero : public BladMat {}; Przykład organizacji rozpoznawania wyjątków: try { // throw …; } catch (DzielZero) {/*…*/} catch (BladMat) {/*…*/} Kolejność bloków catch ma znaczenie przy rozpoznawaniu wyjątków.

Dopasowywanie wyjątków Rozważmy przykład: try { throw E; } catch (H) { // kiedy się tutaj znajdziemy? } H jest tego samego typu co E; H jest jednoznaczną publiczną klasą bazową dla E; H i E są wskaźnikami, a dla typów na które wskazują zachodzi 1) lub 2); H jest referencją, a dla typu do którego się odnosi zachodzi 1) lub 2).

Złapanie każdego wyjątku Można wyłapać każdy wyjątek blokiem catch(...). Blok catch(...) może wystąpić tylko jako ostatni blok. W bloku catch(...) nie jest znany typ wyjątku. W bloku catch można powtórnie zgłosić ten sam wyjątek który właśnie został wyłapany instrukcją throw bez argumentów. Zgłoszenie innego wyjątku w bloku catch można traktować jak podmianę wyjątku (można zmienić nie tylko wartość ale i typ zgłaszanego wyjątku).

Zagnieżdżanie instrukcji try-catch Instrukcję try-catch można umieścić w bloku try – wtedy niewyłapane wyjątki w wewnętrznej instrukcji try-catch będą zgłoszeniem wyjątku w zewnętrznym bloku try. Instrukcję try-catch można również umieścić w bloku catch – wtedy niewyłapane wyjątki w wewnętrznej instrukcji try-catch będą traktowane jak zgłoszenie wyjątku w zewnętrznym bloku catch.

Implementacja mechanizmu rzucania i łapania wyjątków Odwikłanie stosu – wielkie sprzątanie. Po zgłoszeniu instrukcją throw obiekt wyjątku jest umieszczany w pamięci globalnej w specjalnie do tego przeznaczonym miejscu. Wyjątek uznaje się za obsłużony w momencie jego wyłapania przez jakiś blok catch, ale dopiero przy wyjściu z tego bloku wyjątek jest likwidowany. Nie wolno rzucać wyjątków, gdy inny wyjątek jest w trakcie lotu.

Wyjątki w konstruktorach i w destruktorach Nie wolno zgłaszać wyjątków w destruktorach, bo to może powodować problemy przy odwikłaniu stosu. Gdy wyjątek zostanie zgłoszony w konstruktorze, to obiekt nie zostanie utworzony. Gdy wyjątek zostanie zgłoszony w konstruktorze w trakcie inicjalizacji części odziedziczonej lub obiektu składowego, to zainicjalizowana część zostanie automatycznie zlikwidowana. Gdy chcemy zgłosić wyjątek w ciele konstruktora, to najpierw należy zwolnić zasoby przydzielone w ciele konstruktora.

Zdobywanie zasobów poprzez inicjalizację Problem: kiedy funkcja na początku rezerwuje zasób (otwiera strumień, przydziela pamięć, ustawia klucz kontroli dostępu, itp), to może go na końcu nie zwolnić, gdy po drodze zostanie zgłoszony wyjątek. Rozwiązanie: zarządzanie zasobami poprzez opakowywanie ich klasami. Schemat postępowania: w konstruktorze klasy opakowującej zasób zostaje zarezerwowany (gdy rezerwacja się nie powiedzie zostaje zgłoszony wyjątek); klasa opakowująca udostępnia narzędzia do korzystania z zasobu; w destruktorze klasy opakowującej zasób zostaje zwolniony (zadziała również w przypadku zwijania stosu przy zgłoszonym wyjątku).

Zdobywanie zasobów poprzez inicjalizację Przykład zdobywania zasobów poprzez inicjalizację: class plik { FILE *wsk; public: plik (const char *naz, const char *atr) { wsk = fopen(naz,atr); if (!wsk) throw brak_pliku; } ~plik () throw() { fclose(wsk); wsk = 0; } operator FILE* () throw() { return wsk; } };

Zdobywanie zasobów poprzez inicjalizację Wzorzec klasy auto_ptr wspiera technikę zdobywania zasobów poprzez inicjalizację – jego definicja znajduje się w pliku nagłówkowym <memory>. Obiekt auto_ptr jest inicjalizowany wskaźnikiem i można się nim posługiwać w programie jak wskaźnikiem. Konstruktor i przypisanie wzorca auto_ptr zapewniają niejawną konwersję z auto_ptr<P> do auto_ptr<B> o ile można dokonać konwersji z P* do B*. Po skopiowaniu jednego obiektu auto_ptr na drugi, obiekt źródłowy na nic już nie wskazuje. W destruktorze auto_ptr zapewnione jest wywołanie operatora delete na wskazywany obiekt.

Zdobywanie zasobów poprzez inicjalizację Przykład użycia auto_ptr (zakładamy, że okrąg dziedziczy po figura): okrąg *po = new okrąg(123.0); // … auto_ptr<okrąg> apo1 = po; // teraz apo1 jest odpowiedzialny za usunięcie obiektu apo1->r = 123.456; // odniesienie do składowej r w okręgu auto_ptr<okrąg> apo2 = apo1; // przeniesienie własności z apo1 na apo2 // i teraz apo2 jest odpowiedzialny za usunięcie obiektu figura *pf = apo2.get(); // wyłuskanie wskaźnika z auto_ptr // uwaga, teraz apo1.get()==0 auto_ptr<figura> apf = apo2; // przeniesienie własności i konwersja typu // i teraz apf jest odpowiedzialny za usunięcie obiektu

Wyjątki oraz new i delete Funkcje użyte do implementacji operatorów new i delete są zadeklarowane w <new>. Deklaracja tych operatorów jest następująca: void * operator new (size_t) throw(bad_alloc); void operator delete (void *) throw(); void * operator new[] (size_t) throw(bad_alloc); void operator delete[] (void *) throw(); Operator new (oraz new[]) zgłasza wyjątek bad_alloc, gdy nie uda się zarezerwować pamięci na obiekt (tablicę obiektów). Istnieje deklaracja obiektu, który powoduje, że operator new nie zgłasza wyjątku, tylko przekazuje wskaźnik pusty: struct nothrow_t {}; extern const nothrow_t nothrow; Stworzono też specjalne wersje operatora new z parametrem nothrow_t, które zapobiegają zgłaszaniu wyjątków: void * operator new (size_t, const nothrow_t &) throw(); void * operator new[] (size_t, const nothrow_t &) throw();

Wyjątki oraz new i delete Przykład użycia nothrow przy alokacji pamięci: int *p = new int[1000000]; // może zgłosić wyjątek bad_alloc // … // poniższy kod nie zgłosi wyjątku if (int *q = new(nothrow) int[1000]) { // przydział się powiódł } else { //przydział nie powiódł się } Funkcja uncaught_exception() zwraca true, gdy wyjątek zgłoszono ale jeszcze nie wyłapano – umożliwia to specyfikowanie różnych działań w destruktorze zależnie od tego, czy obiekt jest niszczony normalnie, czy w ramach zwijania stosu.

Kończenie programu w krytycznych sytuacjach Funkcja standardowa exit(int) kończy program w łagodny sposób (opróżnia bufory, zamyka pliki, itp). Funkcja standardowa abort() kończy program w drastyczny sposób (bez kosmetyki dotyczącej buforów, plików, itp). Funkcja terminate(), w której jest wywoływana funkcja abort(), jest automatycznie wywoływana w sytuacjach krytycznych takich jak: rzucenie wyjątku, którego nie złapał żaden blok catch; rzucenie wyjątku w trakcie lotu innego wyjątku; rzucenie wyjątku spoza listy kontrolnej. Za pomocą funkcji set_terminate() możemy zmienić uchwyt _uncaught_handler ustalając własną procedurę obsługi zdarzeń krytycznych związanych z wyjątkami. Funkcja ta podmienia uchwyt do funkcji wywoływanej w terminate() – standardowo jest to funkcja abort().

Brak pamięci i operator new Gdy operator new próbuje przydzielić pamięć a wolnej pamięci już nie ma, to zgłasza on wyjątek bad_alloc. Można jednak dostarczyć i wywołać funkcję ratującą, ustawiając uchwyt _new_handler za pomocą set_new_handler() – funkcja ratująca typu void(*)() powinna odzyskać pamięć, a jeśli się to nie uda, to powinna zgłosić wyjątek bad_alloc. Gdy nie wyłapiemy wyjątku bad_alloc, to program zakończy się wywołaniem funkcji terminate().

Brak pamięci i operator new Przykład funkcji ratunkowej w przypadku braku pamięci: void new_hnd () { int bytes = find_mem(); if (bytes<min_alloc) throw bad_alloc; } Przykład użycia funkcji ratunkowej w przypadku braku pamięci: void (*old_hnd)() = set_new_handler(new_hnd); try { // … } catch (bad_alloc) { // … } catch (...) { set_new_handler(old_hnd); throw; } set_new_handler(old_hnd);

Specyfikacja wyjątków Funkcja może wyspecyfikować zbiór wyjątków, które mogą być rzucone w trakcie wykonania funkcji na liście kontrolnej. Lista kontrolna to fraza throw() na końcu nagłówka funkcji – w nawiasie umieszczamy zbiór dopuszczalnych wyjątków. Przykłady list kontrolnych: void K::f (int) const throw(ex1, ex2, ex3); // funkcja składowa f(int) może rzucić // wyjątkiem typu ex1, ex2 albo ex3 double g (double) throw(); // funkcja g(double) nie zgłasza żadnych wyjątków void h (void); // funkcja h() może zgłosić dowolny wyjątek

Specyfikacja wyjątków Nie jest możliwe w czasie kompilacji sprawdzenie każdego naruszenia specyfikacji interfejsu. Jeśli funkcja z listą kontrolną spróbuje zgłosić wyjątek spoza listy, to jest wtedy wywoływana funkcja unexpected(), która domyślnie wywołuje terminate(). Można jednak dostarczyć i wywołać funkcję ratującą, ustawiając uchwyt _unexpected_handler za pomocą set_unexpected() – funkcja ratująca typu void(*)() może na przykład zgłosić wyjątek bad_exception (wyjątek ten trzeba wtedy dopisać do listy kontrolnej) albo wywołać funkcję exit().

Specyfikacja wyjątków Funkcję wirtualną można nadpisać tylko funkcją, której specyfikacja wyjątków na liście kontrolnej jest co najmniej tak restrykcyjna jak jej własna specyfikacja. Przykład nadpisywania funkcji wirtualnych: class B { public: virtual void f (); virtual void g () throw(X,Y); virtual void h () throw(X); }; class P: public B { public: virtual void f () throw(X); // ok virtual void g () throw(X); // ok // virtual void h () throw(X,Y); // błąd – dodanie nowego wyjątku Y do listy };

Wyjątki z biblioteki standardowej Hierarchia klas wyjątków standardowych: exception logic_error runtime_error length_error range_error domain_error overflow_error out_of_range underflow_error invalid_argument bad_cast bad_alloc bad_typeid bad_exception ios_base::failure

Własne wyjątki Wyjątkiem może być dowolny obiekt, ale dobrze jest projektować własną hierarchię klas wyjątków, która dziedziczy po exception. Gdy definiujesz własny wyjątek, pamiętaj aby nie zgłaszał on innych wyjątków w konstruktorach, w destruktorze i w przypisaniu kopiującym. Gdy definiujesz własny wyjątek dziedziczący po exception, pamiętaj aby zdefiniować w nim konstruktor domyślny, konstruktor kopiujący, przypisanie kopiujące, wirtualny destruktor oraz nadpisz metodę what().