Both sides previous revision
Poprzednia wersja
Nowa wersja
|
Poprzednia wersja
Nowa wersja
Both sides next revision
|
pl:dydaktyka:jimp2:2017:labs:wyjatki [2017/04/25 04:20] mwp [Zgłoszenie wyjątku] |
pl:dydaktyka:jimp2:2017:labs:wyjatki [2017/07/17 10:08] 127.0.0.1 edycja zewnętrzna |
... | ... |
| |
} | |
</code> | |
| |
Jeśli dwa różne wyjątki mają wspólną klasę bazową można je przechwycić w pojedynczym bloku catch: | |
<code cpp> | |
int main() { | |
Matrix m1 ... | |
Matrix m2 ... | |
try { | |
m1.div(m2); | |
} catch (const invalid_argument &e) { | |
cerr << e.what() << endl; | |
} catch (...) { | |
//ten blok jest wstanie przechwycić każdy inny wyjątek | |
cerr << "Something went wrong" << endl; | |
} | |
} | } |
</code> | </code> |
Oznacza on mniej więcej tyle: //jeśli pojawił się wyjątek, którego nie mają w parametrach wcześniejsze bloki catch ja się nim zajmę.// **Uwaga** Kolejność bloków catch ma znaczenie! Konstrukcja //catch(...)// pasuje do wszystkich wyjątków, dlatego powinna być umieszczana zawsze jako ostatnia. | Oznacza on mniej więcej tyle: //jeśli pojawił się wyjątek, którego nie mają w parametrach wcześniejsze bloki catch ja się nim zajmę.// **Uwaga** Kolejność bloków catch ma znaczenie! Konstrukcja //catch(...)// pasuje do wszystkich wyjątków, dlatego powinna być umieszczana zawsze jako ostatnia. |
| |
| |
| Jeśli dwa różne wyjątki mają wspólną klasę bazową można je przechwycić w pojedynczym bloku catch: |
| <code cpp> |
| int main() { |
| Matrix m1 ... |
| Matrix m2 ... |
| try { |
| m1.div(m2); |
| } catch (const invalid_argument &e) { |
| cerr << e.what() << endl; |
| } catch (...) { |
| //ten blok jest wstanie przechwycić każdy inny wyjątek |
| cerr << "Something went wrong" << endl; |
| } |
| } |
| </code> |
====Wyjątki nieoczekiwane==== | ====Wyjątki nieoczekiwane==== |
W przypadku kiedy wewnątrz funkcji pojawi się wyjątek, który nie znajduje się w specyfikacji wyjątków danej funkcji, wywoływana jest automatycznie funkcja //unexpected//, która z kolei wywołuje funkcje ustawioną przez //set_unexpected//. Domyślnie funkcją tą jest //terminate// - czyli zakończenie programu. | W przypadku kiedy wewnątrz funkcji pojawi się wyjątek, który nie znajduje się w specyfikacji wyjątków danej funkcji, wywoływana jest automatycznie funkcja //unexpected//, która z kolei wywołuje funkcje ustawioną przez //set_unexpected//. Domyślnie funkcją tą jest //terminate// - czyli zakończenie programu. |
Jeśli zdefiniowana przez programistę funkcja nie rzuca wyjątku określonego w specyfikacji funkcji (patrz [[#specyfikacja_wyjatkow|Specyfikacji wyjątków]], to program po jej wywołaniu i tak zostanie zakończony. | Jeśli zdefiniowana przez programistę funkcja nie rzuca wyjątku określonego w specyfikacji funkcji (patrz [[#specyfikacja_wyjatkow|Specyfikacji wyjątków]], to program po jej wywołaniu i tak zostanie zakończony. |
| |
=====Specyfikacja wyjątków===== | =====noexcept===== |
//Specyfikacja wyjątków// pozwala na zdefiniowanie listy wyjątków, które mogą być zgłoszone przez funkcję. Robi się to przy pomocy operatora //throw// umieszczonego po liście parametrów funkcji: | Specyfikacja noexcept pozwala na jawne zadeklarowanie metody jako nie wyrzucającej wyjątków, wtedy żadna pochodna metoda również nie może wyrzucać wyjątków, wszystkie pozozstałe motedy są traktowane jako potencjlanie wyrzucające wyjątki: |
| |
<code cpp> | <code cpp> |
public: | public: |
... | ... |
// Metoda może zgłosić ejden z dwóch wyjątków określonych | // Metoda potencjalnie wyrzuca wyjątki |
// jako parametry operatora throw. Zakładamy, ze klasy | Matrix div(const Matrix&); |
// WrongDimensionsExcpetion i DivisionByZero są zaimplementowane | // Metoda nie może wyrzucić wyjątku |
Matrix div(const Matrix&) throw (WrongDimensionsExcpetion,DivisionByZero); | virtual ~Matrix() noexcept; |
}; | }; |
| |
</code> | </code> |
| |
Umieszczenie pustej specyfikacji wyjątków (throw()) oznacza, ze funkcja nie powinna zgłaszać żadnych wyjątków. Jeśli jednak wewnątrz funkcji zostanie zgłoszony wyjątek, zostanie wywołana funkcja //unexpected//. Jeśli nie określi się żadnych wyjątków, to znaczy, że metoda może zgłaszać dowolne wyjątki. | |
| |
<code cpp> | |
int myfoo (int param) throw(); // nie można zgłaszać żądnych wyjątków | |
int mybar (int param); // wszystkie wyjątki dozwolone | |
</code> | |
| |
=====Konstruktory, destruktory i wyjątki===== | =====Konstruktory, destruktory i wyjątki===== |
| |
Wyjątki w konstruktorach wyrzuca się w analogiczny sposób jak w //zwykłych// metodach. Kiedy zostanie wyrzucony wyjątek w ciele konstruktora, mechanizm zwijający stos uruchomi destruktory wszystkich obiektów składowych danego obiektu. | Wyjątki w konstruktorach wyrzuca się w analogiczny sposób jak w //zwykłych// metodach. Kiedy zostanie wyrzucony wyjątek w ciele konstruktora, mechanizm zwijający stos uruchomi destruktory wszystkich obiektów składowych danego obiektu. |
| |
Problem pojawia się jednak kiedy w ciele konstruktora dynamicznie alokujemy pamięć. Nie zostanie ona w takim wypadku zwolniona. | W praktyce zawsze należy sprwdzić dziedzinę parametrów przekazanych w konstruktorze i pozowalać na utworzenie jedynie w pełni poprawnego obiektu, namtomiast w przeciwnym wypadku należy wyrzucić wyjątek. |
| |
| Problem pojawia się jednak kiedy w ciele konstruktora dynamicznie alokujemy pamięć. Nie zostanie ona w takim wypadku zwolniona. Chyba, że użyjemy smart pointery. |
| |
====Wyjątki w destruktorze==== | ====Wyjątki w destruktorze==== |
C++ gwarantuje co prawda, że w przypadku zaistnienia takiej sytuacji zostanie wywołana funkcja //terminate()// i program zostanie zamknięty, nie jest to jednak satysfakcjonujące rozwiązanie. | C++ gwarantuje co prawda, że w przypadku zaistnienia takiej sytuacji zostanie wywołana funkcja //terminate()// i program zostanie zamknięty, nie jest to jednak satysfakcjonujące rozwiązanie. |
| |
| Od wersji C++11 destuktory domyślnie są zadeklarowane jako noexcept. |
=====Dziedziczenie i wyjątki===== | =====Dziedziczenie i wyjątki===== |
(:!: do zrozumienia tej sekcji wymagana jest znajomość mechanizmu dziedziczenia w C%%++%% -> laboratorium [[.:dziedziczenie|Dziedziczenie i polimorfizm]]) | (:!: do zrozumienia tej sekcji wymagana jest znajomość mechanizmu dziedziczenia w C%%++%% -> laboratorium [[.:dziedziczenie|Dziedziczenie i polimorfizm]]) |
}; | }; |
| |
void drawBall() throw (BallException){ | void drawBall() { |
throw BallException(); | throw BallException(); |
} | } |
| |
Już podczas kompilacji otrzymujemy ostrzeżenie, że wyjątek //BallException// nigdy nie zostanie wychwycony. Dzieje się tak dlatego, że po napotkaniu pierwszego bloku //catch//, nastąpi automatyczne rzutowanie w górę i dopasowanie //BallException// do //CircleException//. | Już podczas kompilacji otrzymujemy ostrzeżenie, że wyjątek //BallException// nigdy nie zostanie wychwycony. Dzieje się tak dlatego, że po napotkaniu pierwszego bloku //catch//, nastąpi automatyczne rzutowanie w górę i dopasowanie //BallException// do //CircleException//. |
| |
| ===== Warto się zapoznać ===== |
| - [[http://www.exceptionsafecode.com/]] (filmiki z konferencji) |
| - [[http://en.cppreference.com/w/cpp/language/noexcept_spec]] |
| - [[http://www.gotw.ca/publications/mill22.htm]] |
| |
======Ćwiczenia====== | ======Ćwiczenia====== |
- Przetestuj przykład z sekcji [[#dziedziczenie_i_wyjatki|Dziedziczenie i wyjątki]]. Co zrobić, żeby wyjątek //BallException// był łapany poprawnie? | - [1 plus] Przetestuj przykład z sekcji [[#dziedziczenie_i_wyjatki|Dziedziczenie i wyjątki]]. Co zrobić, żeby wyjątek //BallException// był łapany poprawnie? |
- Napisz trzy metody, które będą posiadały wyspecyfkowane po jednym wyjątku jaki mogą wyrzucać. W każdej z metod wyrzuć wyjątek, ale inny od tego jaki został wyspecyfikowany. Za pomocą //set_unexpected// ustaw metodę, która będzie uruchamiana w takim wypadku. **Uwaga**: Program nie może się zakończyć w przypadku wywołania żadnej z 3 funkcji! Jak to osiągnąć pisząc tylko jedną funkcję //unexpected// i pamiętając, że program nie zostanie zatrzymany tylko wtedy kiedy funkcja //unexpected// wyrzuci wyjątek określony w specyfikacji funkcji, która wyrzuciła niepoprawny wyjątek? | - [3 plusy] Napisz klasę //PESEL//, która w konstruktorze przyjmuje ciąg znaków będących numerem PESEL. Klasa powinna posiadać metodę //validatePESEL(const char*)//, która sprawdza czy PESEL jest poprawny według algorytmu podanego na [[http://pl.wikipedia.org/wiki/PESEL|Wikipedii]]. Jeśli przekazany do konstruktora PESEL nie jest poprawny, program powinien wyrzucać wyjątek. Napisz funkcję //main// i przetestuj program. |
- Napisz klasę //PESEL//, która w konstruktorze przyjmuje ciąg znaków będących numerem PESEL. Klasa powinna posiadać metodę //validatePESEL(const char*)//, która sprawdza czy PESEL jest poprawny według algorytmu podanego na [[http://pl.wikipedia.org/wiki/PESEL|Wikipedii]]. Jeśli przekazany do konstruktora PESEL nie jest poprawny, program powinien wyrzucać wyjątek. Napisz funkcję //main// i przetestuj program. | - [3 plusy] Dla klasy Student dopisać metody walidujące argumenty przekazywane do klasy. W taki sposób by uniemożliwić storzenie niepoprawnego stanu obiektu. |
- Napisz program, który będzie służył do opóźniania, lub przyspieszania wyświetlania napisów do filmów w formacie MicroDVD ;-). Czas pojawienia się napisu oznaczany jest w tym formacie dwiema liczbami umieszczonymi pomiędzy nawiasami klamrowymi. Pierwsza liczba to numer klatki pojawienia się napisu, druga to numer klatki zniknięcia. Program powinien posiadać metodę //delay(const char* in, const char* out,int delay, int fps)// wykonującą opóźnienie (lub przyspieszenie) napisów o podaną ilość milisekund w zależności od tego jaki film ma //framerate//. \\ Program powinien przyjmować jako parametry cztery wartości: ścieżkę do pliku wejściowego, ścieżkę do pliku wyjściowego i liczbę milisekund i //framerate//. Metoda //delay// powinna wyrzucać wyjątek gdy w pliku pojawi się niepoprana sekwencja znaków. Np. zamiast {1234}{4565} pojawia się {sdfg}{33ff}. | * Student musi mieć co najmniej imię i nazwisko, każdy człon imienia musi być napisany z wielkiej litery pozostałe litery małe. Imię nie może zawierać cyfr i znaków specjalnych (amperad, dolar, procent, itp...) |
- Jeśli masz dostępną swoją klasę Matrix (laboratorium [[.:klasy2|Klasy i obiekty II]]), dopisz dla niej obsługę wyjątków. | * Wiek studenta musi mieścić się w zakresie 10 do 100 lat |
| * kierunek studiów musi zawierać się w zbiorze oferowanym akutalnie przez uczelnie XYZ: informatyka, ekonomia, matematyka, fizyka, filozofia |
| * w każdym wypadku, kiedy zostanie naruszony któryś z warunków należy zwrócić wyjątek dziedziczący po klasie invalid_argument, odpowiednio: |
| * InvalidNameSurname, InvalidNameCharacters, InvalidAge, InvalidProgram |
| * dopisać main z możliwością wczytywania danych o studencie i tworzącym studentów w pętli wstawiającym poprawnie utworzone obiekty do repozytorium. |
| - **[5 punktów] Napisz program, który będzie służył do opóźniania, lub przyspieszania wyświetlania napisów do filmów w formacie MicroDVD ;-). Czas pojawienia się napisu oznaczany jest w tym formacie dwiema liczbami umieszczonymi pomiędzy nawiasami klamrowymi. Pierwsza liczba to numer klatki pojawienia się napisu, druga to numer klatki zniknięcia. Program powinien posiadać metodę //delay(const char* in, const char* out,int delay, int fps)// wykonującą opóźnienie (lub przyspieszenie) napisów o podaną ilość milisekund w zależności od tego jaki film ma //framerate//. \\ Program powinien przyjmować jako parametry cztery wartości: ścieżkę do pliku wejściowego, ścieżkę do pliku wyjściowego i liczbę milisekund i //framerate//. Metoda //delay// powinna wyrzucać wyjątek gdy w pliku pojawi się niepoprana sekwencja znaków. Np. zamiast {1234}{4565} pojawia się {sdfg}{33ff}.** |