Konwersje. Programowanie obiektowe (2005/2006) Wykład 4

Podobne dokumenty
Język C++ wykład VIII

Obsługa wyjątków. Język C++ WW12

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Wyjątki. Wyjątki. Bogdan Kreczmer. Katedra Cybernetyki i Robotyki Politechnika Wrocławska

Programowanie w języku C++

Wyjątki (exceptions)

Operacje wejścia/wyjścia (odsłona druga) - pliki

Szablony klas, zastosowanie szablonów w programach

Programowanie w C++ Wykład 11. Katarzyna Grzelak. 13 maja K.Grzelak (Wykład 11) Programowanie w C++ 1 / 30

DYNAMICZNE PRZYDZIELANIE PAMIECI

Programowanie w C++ Wykład 12. Katarzyna Grzelak. 28 maja K.Grzelak (Wykład 12) Programowanie w C++ 1 / 27

Programowanie w C++ Wykład 6. Katarzyna Grzelak. kwiecień K.Grzelak (Wykład 6) Programowanie w C++ 1 / 40

Kurs programowania. Wykład 3. Wojciech Macyna. 22 marca 2019

PARADYGMATY PROGRAMOWANIA Wykład 3

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

Efekty uboczne błędów

PARADYGMATY PROGRAMOWANIA Wykład 4

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

Programowanie w C++ Wykład 8. Katarzyna Grzelak. 7 maja K.Grzelak (Wykład 8) Programowanie w C++ 1 / 31

Podstawy programowania skrót z wykładów:

4. Wyrzuć wyjątek jeśli zmienna ist nie istnieje bloki: try, catch i wyrzucanie wyjątku

Programowanie w językach

jest mocny, skoro da się w nim wyrazić nowe pojęcia; łatwiej przenieść go na nową platformę jest mniejszy.

C++ - [3-5] Pliki i strumienie w C++

EGZAMIN PROGRAMOWANIE II (10 czerwca 2010) pytania i odpowiedzi

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

Programowanie obiektowe - Przykładowe zadania egzaminacyjne (2005/2006)

Lab 9 Podstawy Programowania

Zaawansowane programowanie w języku C++ Wyjątki

Programowanie w C++ Wykład 11. Katarzyna Grzelak. 21 maja K.Grzelak (Wykład 11) Programowanie w C++ 1 / 24

Wykład 8: Obsługa Wyjątków

Programowanie w C++ Wykład 14. Katarzyna Grzelak. 3 czerwca K.Grzelak (Wykład 14) Programowanie w C++ 1 / 27

Podstawy programowania w języku C++

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

Rzutowanie i konwersje

Throwable. Wyjatek_1(int x_) { x = x_; } int podaj_x()

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

Programowanie w języku C++

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) { zdefiniuje. Integer::operator=(ri);

Wskaźniki. Przemysław Gawroński D-10, p marca Wykład 2. (Wykład 2) Wskaźniki 8 marca / 17

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

1 Wskaźniki. 1.1 Główne zastosowania wskaźników

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Język C zajęcia nr 11. Funkcje

Wyjątki. Streszczenie Celem wykładu jest omówienie tematyki wyjątków w Javie. Czas wykładu 45 minut.

ZASADY PROGRAMOWANIA KOMPUTERÓW

TEMAT : KLASY DZIEDZICZENIE

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

Część 4 życie programu

Programowanie w języku C++

Programowanie Obiektowe i C++

WYJĄTKI. Jest ona jednak czasochłonna i prowadzi do duŝego zapotrzebowania na zasoby systemu.

Programowanie, część I

Wykład II. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Zad.30. Czy można utworzyć klasę, która implementuje oba interfejsy?

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

> C++ wskaźniki. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki 26 kwietnia 2017

ATD. Wykład 8. Programowanie (język C++) abstrakcyjny typ danych. Abstrakcyjne typy danych (ATD) Metody czysto wirtualne. Definicje i uwagi:

Programowanie obiektowe Wykład 3. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/21

Programowanie w C++ Wykład 12. Katarzyna Grzelak. 20 maja K.Grzelak (Wykład 12) Programowanie w C++ 1 / 32

Programowanie w C++ Wykład 9. Katarzyna Grzelak. 14 maja K.Grzelak (Wykład 9) Programowanie w C++ 1 / 30

Podstawy programowania w języku C++

Programowanie w C++ Wykład 8. Katarzyna Grzelak. 15 kwietnia K.Grzelak (Wykład 8) Programowanie w C++ 1 / 33

ALGORYTMY I STRUKTURY DANYCH

Przykład klasa biblioteczna string

Programowanie obiektowe 2005/2006. Laboratorium 1. Przeciążanie funkcji

Funkcje przeciążone, konstruktory kopiujące, argumenty domyślne

wykład IV uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C, a C++. wykład IV dr Jarosław Mederski Spis Język C++ - wstęp

Programowanie obiektowe w C++ Wykład 12

Języki i metody programowania Java INF302W Wykład 3 (część 1)

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy WSKAŹNIKI KLASOWE

Pliki wykład 2. Dorota Pylak

Programowanie komputerowe. Zajęcia 3

1 P roste e t ypy p d a d n a ych c - c ąg ą g d a d l a szy 2 T y T py p z ł z o ł żo ż ne e d a d n a ych c : T BLICE

Wprowadzenie w dziedziczenie. Klasa D dziedziczy klasę B: Klasa B klasa bazowa (base class), klasa D klasa pochodna (derived class).

Programowanie w języku Java - Wyjątki, obsługa wyjątków, generowanie wyjątków

Klasyfikacja wyjątków

Wstęp do Programowania 2

Wstęp do programowania obiektowego. Przekazywanie parametrów do funkcji w C++ Metody i funkcje operatorowe Strumienie: standardowe, plikowe, napisowe

KURS C/C++ WYKŁAD 8. Deklaracja funkcji informuje komplilator jaką wartość funkcja będzie zwracała i jakiego typu są jej argumenty.

Zadania z podstaw programowania obiektowego

Wejście wyjście strumieniowe

Przeciążenie operatorów

Operator przypisania. Jest czym innym niż konstruktor kopiujący!

Wykład I. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Programowanie obiektowe w języku C++ dr inż. Jarosław Forenc

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

Tbli Tablice obiektów biktó są tworzone dokładnie d tak samo, jak i tablice, składające się z elementów innego typu

Podstawy Informatyki. Inżynieria Ciepła, I rok. Wykład 10 Kurs C++

Wzorce funkcji (szablony)

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

ROZDZIAŁ 2. Operatory

Wykład :37 PP2_W9

Informatyka I. Typy danych. Operacje arytmetyczne. Konwersje typów. Zmienne. Wczytywanie danych z klawiatury. dr hab. inż. Andrzej Czerepicki

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 16 kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 27

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

Wstęp do programowania

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 26 marca kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 40

Transkrypt:

Konwersje Konwersja typu ma miejsce wtedy, kiedy wspólnie korzysta się ze zmiennych lub obiektów różnych typów, na przykład w instrukcjach przypisania, w wyrażeniach, podczas przekazywania argumentów aktualnych do wywoływanej funkcji. Konwersja typu może być: niejawna (ang.implicite) wykonywana automatycznie przez kompilator. jawna (ang. explicite) wykonywana na życzenie użytkownika, Konwersja niejawna wykonywana jest przez kompilator wtedy, kiedy wynika to z kontekstu: w wyrażeniach arytmetycznych (konwersja jednego argumentu na typ drugiego argumentu, zgodnie z regułami zdefiniowanymi w języku). int i=2; double d=2.5; cout<< (i+d) <<endl; // i zostanie przekształcone do double: 2. w instrukcji przypisania (na typ występujący po lewej stronie operatora =) int i; double d=2.5; i=d; /* d zostanie obcięte do typu int: 2 */ w instrukcji wywołania funkcji (na typ występujący w prototypie funkcji) extern double sqrt(double); // 2 jest przekształcone do double: 2. cout << "Pierwiastek z 2 to: " << sqrt(2) << endl; podczas zwracania wartości wyznaczonej w funkcji, której typ jest różny od typu zadeklarowanego w nagłówku funkcji double roznica(int x, int y) // wynik zostanie przekształcony do double return x-y; Konwersja jawna wykonywana jest w wyniku użycia operatora konwersji. Jawna konwersja w stylu języka C wymaga podania typu w nawiasach: double z=6.0 int k; k=(int)z; // lub k=int(z); z=(double)k; Stara składnia języka C++ : double z=6.0 int k; k=int(z); z=double(k); W standardzie języka C++ wprowadzono nazwane operatory rzutowania: static_cast - konwersja typów pokrewnych, np. double do int dynamic_cast - konwersja kontrolowana w czasie wykonywania programu const_cast - konwersja usuwająca kwalifikator const reinterpret_cast - konwersja typów nie spokrewnionych, np. wskaźnik do nie spokrewnionego typu wskaźnikowego 1

Składnia instrukcji z rzutowaniem ma postać: operator_rzutowania < typ konwersji> (wyrażenie); Przykład: double d=2.58; int i = static_cast<int>d; int zaokr(float f) return static_cast<int> (f+0.5); double d=2.58; int i = (int)d; int zaokr(float f) return int(f+0.5); Konwersje związane z obiektami Możliwe są następujące przypadki typ wbudowany na obiekt należy zdefiniować odpowiedni konstruktor obiekt na typ wbudowany - należy zdefiniować funkcję rzutowania obiekt jednej klasy na obiekt inne klasy - odpowiedni konstruktor lub funkcja rzutowania 2

Konwersja typu wbudowanego na typ klasy Wszystkie konstruktory z jednym argumentem typu wbudowanego realizują konwersję tego typu na typ klasy. class Punkt int x, y ; public : Punkt (int xx=0, int yy=0) x = xx ; y = yy ; Punkt (const Punkt & p) x = p.x ; y = p.y ; ; void fun (Punkt p) cout << "** funkcja fun " << "\n" ; W main() void fun (Punkt) ; Punkt a(3,4) ; a = Punkt (12) ; // konwersja: jawne wywołanie konstruktora, // utworzony zostanie obiekty tymczasowy typu Punkt, // następnie zostanie przypisany obiektowi a a = 12 ; fun(4) ; // konwersja: wywołanie niejawne konstruktora: // a = Punkt(12) // konwersja: wywołanie niejawne konstruktora, // konstruktor kopiujący nie będzie używany Załóżmy, że oprócz konstruktora jednoargumentowego zdefiniowano również operator przypisania liczby typu int obiektowi klasy Punkt. Który z nich będzie użyty w przypadku instrukcji: a = 12; Konwersja z użyciem konstruktora jest wykonywana niejawnie. Można zabronić używania konstruktora do konwersji niejawnych. Służy do tego słowo kluczowe explict: class Punkt: public: explicit Punkt(int); ; W main(): a=12; // zostanie odrzucone a=punkt(3); // OK. trzeba podać jawnie Punkt x(1,1), y(2,5); a=b+5; // błąd a=b+punkt(5); // OK. podano jawnie 3

Konwersja typu klasy na typ wbudowany Tego typu konwersja wymaga zdefiniowania funkcji operatora rzutowania. Przykład: class Punkt int x,y; Aby w main() można było dokonywać konwersji: Punkt a(2,3); int n; n = int(a); // zapis jawny funkcji operatora rzutowania n = b; // zapis niejawny trzeba zdefiniować funkcję rzutowania, która jest składową klasy: operator int(); // UWAGA: w funkcji operatora konwersji // nie podaje się typu i ma postać następującą: Punkt::operator int() return x; Przebieg konwersji: wywołanie funkcji operatora rzutowania, która przekształca obiekt w zmienną typu podstawowego, następnie wykonanie zwykłego podstawienia. Przykład: niejawne wywołanie operatora rzutowania gdy przekazywany jest obiekt do funkcji class Punkt int x, y ; public : Punkt (int xx=0, int yy=0) x = xx ; y = yy ; Punkt (const Punkt & p) x = p.x ; y = p.y ; operator int() return x ; ; // konstruktor // konstruktor kopiujący // operator rzutowania void fun (int n) cout << "** wywolanie funkcji z argumentem : " << n << endl ; int main() Punkt a(3,4) ; fun (a) ; Która z funkcji (konstruktor kopiujący, operator rzutowania) zostanie użyta przy przekazywaniu obiektu a do funkcji? A może obydwie? 4

Przykład: niejawne wywołanie operatora rzutowania podczas obliczania wartości wyrażenia class Punkt int x, y ; public : Punkt (int xx=0, int yy=0) x = xx ; y = yy ; operator int() return x ; ; Co wydrukuj poniższy program? int main() Punkt a(3,4), b(5,7) ; int n1, n2 ; n1 = a + 3 ; cout << "n1 = " << n1 << "\n" ; n2 = a + b ; cout << "n2 = " << n2 << "\n" ; double z1, z2 ; z1 = a + 3 ; cout << "z1 = " << z1 << "\n" ; z2 = a + b ; cout << "z2 = " << z2 << "\n" ; return 0; Jak to działa: Kompilator po napotkaniu operatora dodawania, w którym jednym z argumentów jest obiekt klasy sprawdza, czy nie ma przeciążonego operatora dodawania. Jeśli go nie znajdzie, szuka operatora rzutowania, który pozwoli mu wykonać działanie ze standardowym operatorem dodawania. Przykład: konwersja ułamka do typu double (stara i nowa składnia) Ulamek::operator double() return double(licznik)/double(mianownik); lub Ulamek::operator double() return static_cast<double>(licznik)/static_cast<double>(mianownik); Użycie: Ulamek x(22,7); if (z>3.14 && z < double(x)) Ulamek x (22,7); if (z>3.14 && z < static_cast<double>(x)) Ulamek x (22,7); if (z>3.14 && z < x) 5

Konwersja typu jednej klasy na typ innej klasy Przykład: chcemy dokonywać konwersji obiektu typu Punkt na obiekt typu liczby zespolonej. Wariant 1: można w klasie A zdefiniować operator rzutowania wykonujący konwersję A na B: class zespolona ; class Punkt int x, y ; public : Punkt (int xx=0, int yy=0) x=xx ; y=yy ; operator zespolona () ; // konwerja Punkt --> zespolona ; class zespolona double rzeczywista, urojona ; public : zespolona (double r=0, double i=0) rzeczywista=r ; urojona=i ; friend Punkt::operator zespolona () ; void drukuj () cout << rzeczywista << " + " << urojona <<"i\n" ; ; Punkt::operator zespolona () zespolona r ; r.rzeczywista=x ; r.urojona=y ; return r ; main() Punkt a(2,5) ; zespolona c ; c = (zespolona) a ; c.drukuj () ; // konwersja jawna Punkt b (9,12) ; c = b ; c.drukuj () ; // konwersja niejwana 6

Wariant 2: za pomocą konstruktora; konstruktor klasy A, który otrzymuje argument klasy B wykonuje konwersję B na A.: class Punkt ; class Zespolona double rzeczywista, urojona ; public : Zespolona (double r=0, double i=0) rzeczywista=r ; urojona=i ; Zespolona (Punkt) ; void drukuj () cout << rzeczywista << " + " << urojona << "i\n" ; ; class Punkt int x, y ; public : Punkt (int xx=0, int yy=0) x=xx ; y=yy ; friend Zespolona::Zespolona (Punkt) ; ; Zespolona::Zespolona (Punkt p) rzeczywista = p.x ; urojona = p.y ; main() Punkt a(3,5) ; Zespolona c (a) ; c.drukuj () ; 7

Obsługa błędów i sytuacji wyjątkowych Wyjątek (ang. exception) to zdarzenie spowodowane przez anormalną sytuację, która nie może wystąpić podczas zwykłego wykonywania programu. Tradycyjna obsługa błędów Tradycyjne podejście do zaprogramowania reakcji na błędy: Zaniechanie wykonania programu i wysłanie komunikatu o błędzie. Przekazanie do programu wartości reprezentującej błąd. Zignorowanie błędu. Przekazanie do programu poprawnej wartości i przesłanie informacji o błędzie przez specjalną zmienną. Wywołanie specjalnie napisanej procedury obsługi błędu. Co można wykorzystywać? Standardowe wyjście diagnostyczne cerr (ang. standard error). Wyjście to nie jest buforowane. Funkcja exit (Należy dołączyć plik nagłówkowy <cstdlib> lub <stdlib.h> ). Spowoduje zakończenie programu, wywołane zostaną tylko destruktory obiektów globalnych i statycznych. Funkcja abort : (Należy dołączyć plik nagłówkowy <cstdlib> lub <stdlib.h> ). Spowoduje zakończenie programu, nie wywołane zostaną żadne destruktory. Makroinstrukcja assert (Należy dołączyć plik nagłówkowy <cstdlib> lub <assert.h> ) Spowoduje zakończenie wykonywania programu, jeśli nie jest spełniony podany waunek. Przykład: void f(int *wsk) assert(wsk!= 0); // wywołaj abort(), jeśli wsk==0 8

Model obsługi wyjątków (błędów) w języku C++ Wyjątek to obiekt, który jest przekazywany z obszaru programu, w którym pojawi się problem do tej części programu, w której problem zostanie obsłużony. W skład mechanizmu obsługi wyjątków wchodzą: miejsca zgłoszenia wyjątku do zgłoszenia wyjątku służy słowo throw procedury przechwytujące i obsługujące wyjątki, procedura obsługująca wyjątek rozpoczyna się od słowa catch bloki try, w których mogą być uaktywnione wyjątki int f1() try f2(); catch() int f2() f3(); int f3() throw 1; 9

Przykłady Przykład: wychwytywanie dzielenia przez 0, wersja A - wyjątek jest napisem double iloraz(double dzielna, double dzielnik) if (dzielnik == 0) // zgłoszenie wyjątku throw "proba dzielenia przez zero"; return dzielna/dzielnik; int main() double a,b,wynik; cout << "Wpisz dwie liczby (EOF - koniec) "; while ( cin >> a >> b) // w tym obszarze może pojawić się dzielenie przez zero try wynik=iloraz(a,b); cout << "Iloraz= " << wynik << endl; // obsluga wyjatku typu napis catch (const char * s) cout << "Wystapil wyjatek: " << s << endl; cout << "Wpisz wpisz nastepny zestaw liczb (EOF - koniec) "; cout << "Koniec programu" << endl; return 0; 10

Przykład: wychwytywanie dzielenia pr zez 0, wersja B wyjątek jest obiektem własnej klasy /* klasa do obsługi wyjątków */ class DzieleniePrzezZero private: const char *komunikat; public: DzieleniePrzezZero(const char *tekst) komunikat = tekst; const char *jaki() const return komunikat; ; Punkt zgłoszenia wyjątku, sterowanie wraca do funkcji wywołującej W tym fragmencie można oczekiwać zgłaszania wyjątków Procedura obsługi wyjątku typu DzieleniePrzezZero double iloraz(double dzielna, double dzielnik) if (dzielnik == 0) throw DzieleniePrzezZero("proba dzielenia przez zero"); return dzielna/dzielnik; int main() double a,b,wynik; cout << "Wpisz dwie liczby (EOF - koniec) "; while ( cin >> a >> b) try wynik=iloraz(a,b); cout << "Iloraz= " << wynik << endl; catch (DzieleniePrzezZero wyj) cout << "Wystapil wyjatek: " << wyj.jaki() << endl; cout << "Wpisz dwie liczby (EOF - koniec) "; cout << "Koniec programu" << endl; return 0; 11

Przykład: klasa do działań na wektorach, wychwytywanie przekroczenia indeksu za pomocą wyjątku #include <cstdlib> // klasa do obsługi działań na wektorach class wektor int l_elem ; int * wsk ; public : wektor (int) ; ~wektor () ; int & operator [] (int) ; ; // klasa do obsługi wyjatkow zwiazanych // z przekroczeniem zakresu class wektor_zakres ; wektor::wektor (int n) wsk = new int [l_elem = n] ; wektor::~wektor () delete wsk ; Punkt zgłoszenia wyjątku, sterowanie wraca do funkcji wywołującej W tym fragmencie można oczekiwać zgłaszania wyjątków Procedura obsługi wyjątku int & wektor::operator [] (int i) if (i<0 i>l_elem) wektor_zakres wekt ; throw (wekt) ; return wsk[i] ; main () try wektor v(10) ; v[11] = 5 ; // indeks poza zakresem catch (wektor_zakres wekt) cout << "wyjatek: przekroczenie zakresu \n" ; exit (1) ; 12

Przykład: Przykład: klasa do działań na wektorach, wychwytywanie błędnej inicjalizacji wektora oraz przekroczenia indeksu za pomocą wyjątków. #include <stdlib.h> class wektor int l_elem ; int * wsk ; public : wektor (int) ; ~wektor () ; int & operator [] (int) ; ; // Obsługa wyjątków związanych z podaniem złej liczby składowych class wektor_utworz public : int ile ; // liczba żądanych składowych wektor_utworz (int i) ile = i ; ; // Obsługa wyjątków związanych z przekroczeniem zakresu class wektor_zakres public : int poza ; wektor_zakres (int i) poza = i ; ; wektor::wektor (int n) if (n <= 0) wektor_utworz w(n) ; // błąd: liczba elementów > 0 throw w ; wsk = new int [l_elem = n] ; wektor::~wektor () delete wsk ; int & wektor::operator [] (int i) if (i<0 i>l_elem) wektor_zakres w(i) ; // błąd: przekroczony zakres indeksów throw w ; return wsk[i] ; int main () try wektor v(-3) ; // wyjatek: wektor_utworz v[11] = 5 ; // wyjatek: wektor_zakres catch (wektor_zakres w) cout << "wyjatek: indeks " << w.poza << " poza zakresem \n" ; exit (1) ; catch (wektor_utworz w) cout << "wyjatek: proba utworzenia wektora o liczbie elementow = " << w.ile << endl ; exit (2) ; 13

Obsługa dowolnego wyjątku Dowolny wyjątek reprezentuje trójkropek: catch () // instrukcje Jeśli jest wykorzystywany, powinien znajdować się na końcu zestawu bloków catch. Ponowne zgłaszanie wyjątku Czasem jeden blok catch nie jest w stanie w pełni obsłużyć wyjątku. Wtedy w bloku catch można ponownie zgłosić wyjątek. Służy do tego wyrażenie: throw; Określanie typów wyjątków zgłaszanych w funkcji W prototypie funkcji można określić typy wyjątków, które mogą być zgłaszane w tej funkcji: void f() throw (A,B); funkcja może zgłaszać wyjątki typu A i Klauzulę throw należy powtórzyć w nagłówku funkcji podczas jej definicji. Powyższy zapis jest równoważny następującemu: void f() try catch (A a) throw ; catch (B b) throw ; catch () unexpected() ; // wyjątek nieoczekiwany 14

Przykład: ponowne zgłaszanie wyjątku #include <fstream> #include <cstdlib> char NazwaPliku[]="test.txt"; /* zapisz tablicę do pliku */ void Zapis(fstream &f,double *Tab, int Rozmiar) for(int i=0;i<rozmiar;++i) f.write((char*)(tab+i),sizeof(double)); if(!f.good()) throw "Blad zapisu"; /* zapisz do tablicy */ void Zapis(double *Tab,int Rozmiar) fstream f; f.open(nazwapliku,ios::out); if(!f.good()) throw "Nie mozna otworzyc do zapisu"; try Zapis(f,Tab,Rozmiar); catch() f.close(); // zamykamy plik throw; // zgłaszamy ponownie /* czytaj do tablicy z pliku */ void Odczyt(fstream &f,double *Tab,int Rozmiar) for(int i=0;i<rozmiar;++i) f.read((char*)(tab+i),sizeof(double)); if(!f.good()) throw "Blad odczytu"; // zgłoszony wyjątek /* czytaj do tablicy */ void Odczyt(double *Tab,int Rozmiar) fstream f; f.open(nazwapliku,ios::in); if(!f.good()) throw "Nie mozna otworzyc do odczytu"; // zgłoszony wyjątek try Odczyt(f,Tab,Rozmiar); catch() f.close(); // zamykamy plik throw; // zgłaszamy ponownie int main() const int R=100; double Tab[R]; try Zapis(Tab,R); cout<<"\rzapisano poprawnie"<<endl; cout<<"nacisnij <Enter> "; cin.get(); Odczyt(Tab,R); cout<<"\rodczytano poprawnie"<<endl; catch(const char *Blad) cout<<"wyjatek: "<< Blad<<endl; return 0; 15

Niepowodzenia przydziału pamięci Niepowodzenia przydziału pamięci można obsługiwać za pomocą kilku metod: Standard języka C++ przewiduje zgłoszenie wyjątku bad_alloc, gdy nie powiedzie się new. Wiele kompilatorów nie jest jeszcze zgodnych z tym standardem, new w przypadku niepowodzenia zwraca 0. Standard języka C++ pozwala używać wersję new, która zwraca 0 w przypadku niepowodzenia, ale trzeba to odpowiednio zgłosić kompilatorowi. Można również wykorzystać do obsługi niepowodzeń new funkcję set_new_handler. Przykład 1: Program automatycznie zgłasza wyjątek klasy bad_alloc. W klasie tej zdefiniowana jest funkcja what(), która pozwala wyprowadzić tekst opisujący wyjątek. #include <cstdlib> #include <new> int main() long rozmiar ; int * wsk ; int nr_bloku ; try cout << "Rozmiar bloku pamieci? " ; cin >> rozmiar ; for (nr_bloku=1 ; ; nr_bloku++) wsk = new int [rozmiar] ; cout << "Przydzielono blok nr : " << nr_bloku << "\n" ; catch (bad_alloc & wyjatek) cout << "Wystapil wyjatek: " << wyjatek.what() << endl; exit (1); // instrukcje return 0; Przykład 2: Program zgłasza wyjątek typu bad_alloc, ale nie chcemy z tego korzystać. Wyłączamy zgłaszanie wyjątku. #include <cstdlib> #include <new> int main() long rozmiar ; int * wsk ; int nr_bloku ; cout << "Rozmiar bloku pamieci? " ; cin >> rozmiar ; for (nr_bloku=1 ; ; nr_bloku++) wsk = new (nothrow) int [rozmiar] ; if (wsk == 0) cout << "Brak pamieci\n" ; cout << "Zakoncz wykonywanie\n" ; exit (1) ; cout << "Przydzielono blok nr : " << nr_bloku << "\n" ; 16

Przykład 3: Korzystanie z set_new_handler Funkcja set_new_handler() pozwala określić funkcję, która będzie wywołana w przypadku nieuzyskania przydziału pamięci za pomocą new. #include <cstdlib> #include <new> // dla set_new_handler int main() // prototyp funkcji wywoływanej, gdy brak pamieci void brak_pamieci () ; // set_new_handler pozwala zarejestrować procedurę obsługi set_new_handler (brak_pamieci) ; long rozmiar ; int * wsk ; int nr_bloku ; cout << "Rozmiar bloku pamieci? " ; cin >> rozmiar ; for (nr_bloku=1 ; ; nr_bloku++) wsk = new int [rozmiar] ; cout << "Przydzielono blok nr : " << nr_bloku << "\n" ; void brak_pamieci () cout << "Brak pamieci\n" ; cout << "Zakoncz wykonywanie\n" ; exit (1) ; 17

Wyjątki a konstruktory i destruktory Konstruktor nie zwraca wartości. Tradycyjne zgłosznie błędu w konstrukorze to: ustawienie nielokalnej flagi i oczekiwanie, że użytkownik sprawdzi jej wartość zwrócenie częściowo poprawnie utworzonego obiektu i oczekiwanie, że użytkownik sprawdzi poprawność tego obiektu Obsługa wyjątków w konstruktorze C++ gwarantuje, że przy wychodzeniu z obszaru zasięgu dla wszystkich obiektów, których konstruktory zostały wykonane do końca, wykonane zostaną destruktory. Jeśli konstruktor nie zostanie wykonany do końca i zostanie wyrzucony wyjątek, to dla danego obiektu nie zostanie wywołany odpowiadający mu destruktor. Jeśli w konstruktorze przydzielana jest pamięć, destruktor nie będzie mógł jej zwolnić. Rozwiązanie najprotsze: przechwytywanie wyjątków w konstruktorze i tam zwalnianie zasobów Obsługa wyjątków w destruktorze Należy unikać wyrzucania wyjątków w destruktorach. Jeśli jest niezbędny, musi być obsłużony w destruktorze. 18

Błędy we-wy Stan strumienia Z każdym strumieniem wejścia lub wyjścia jest związany jego stan. Właściwe ustawienie i testowanie stanu strumienia umożliwia obsługę błędów i warunków niestandardowych. Stan strumienia jest reprezentowany za pomocą zbioru flag (znaczników): Nazwa bitu goodbit eofbit failbit badbit Znaczenie goodbit = 0 oznacza, że wszystko jest w porządku (nie jest ustawiony żaden z pozostałych bitów) Bit ustawiany po napotkania końca pliku Bit ustawiany wskutek odczytania danych innego typu niż oczekiwany, również podczas problemów przy zapisywaniu danych, można nadal używać strumienia błąd dotyczy ostatniej operacji Bit ustawiany w przypadku podejrzenia uszkodzenia strumienie (na przykład problemów z odczytem pliku), nie można już używać strumienia, błąd krytyczny Wartości znaczników moża odczytywać i ustawiać za pomocą funkcji składowych klasy ios: Nazwa funkcji good() eof() fail() bad() rdstate() clear(int=0) Działanie Zwraca true, gdy wszystko jest w porządku (wszystkie bity stanu strumienia są wyzerowane) Zwraca true, jeśli napotlany zostanie oniec pliku (ustawiony eofbit) Zwraca true, jeśli wystąpi błąd (ustawiony failbit) Zwraca true, jeśli wystąpi błąd krytyczny (ustawiony badbit) Zwraca stan znaczników strumienia Czyści wszystkie bity stanu strumienia i ustawia je zgodnie z podanym argumentem setstate(state) Ustawia bity określone w argumencie Przykłady: f1 oznacza strumień f1.clear(); // wyczyści wszystkie bity stanu f1.clear(badbit); // ustawi bit badbit, zaś wszystkim innym przypisze 0 f1.clear(badbit f1.rdstate()); // ustawi bit badbit, // wszystkie inne pozostawi nie zmienione Znaczniki są zdefiniowane w klasie ios_base (dawniej ios). Pełna nazwa znacznika to: std::ios_base::eofbit std::ios::eofbit (ze względów zgodności wstecz) 19

Przeciążone operatory () oraz! Do badania stanu strumienia zdefiniowano przeciążone operatory () oraz!. Jeśli f1 oznacza strumień, to (f1) : przyjmuje wartość prawda, jeśli żaden z bitów błędu nie jest ustawiony (good() zwraca 1) w przeciwnym wypadku przyjmuje wartość nieprawda Jeśli f1 oznacza strumień, to!f1 : przyjmuje wartość nieprawda, jeśli jeden z bitów błędu jest ustawiony (good() zwraca 0) w przeciwnym wypadku (żaden z bitów błędu nie jest ustawiony) przyjmuje wartość nieprawda Badanie stanu strumienia Sprawdzenie, czy można utworzyć plik ofstream wyjscie ("wynik", ios::out ios::binary); if (!wyjscie) cout << "Nie mozna utworzyc pliku \n" ; exit (1) ; Potwierdzenie, że zakończono wczytywanie danych z powodu napotkania końca pliku while (cin >> liczba) suma += liczba; if (cin.eof()) cout << "Zakonczono wczytywanie danych : koniec pliku"<< endl; Aby można było wczytywać nowe dane ze strumienia cin, trzeba wyczyścić bit eof: while (cin >> liczba) suma += liczba; if (cin.eof()) cout << "Zakonczono wczytywanie danych : koniec pliku"<< endl; // // instrukcje // cout << "Nowe dane:" << endl; cin.clear(); while (cin >> liczba) suma += liczba; 20

Operacje we/wy a wyjątki Strumienie we/wy domyślnie nie generują wyjątków. Istnieje jednak możliwość określenia dla każdego bitu stanu czy jego ustawienie ma spowodować automatycznie wygenerowanie wyjątku. Służy do tego funkcja składowa exceptions(). Wygenerowany wyjątek jest obiektem klasy ios_base::failure. Przykłady: cout.exceptions(ios::eofbit); // generuj wyjątek dla bitu końca pliku cout.exceptions(ios::eofbit ios::failbit ios::badbit); // generuj wyjątek dla wszystkich bitów stanu Przykład #include <exception> int main() cin.exceptions(ios::failbit); cout.precision(2); cout << showpoint << fixed; cout << "Wpisz liczby: "; double suma = 0.0; double liczba; try while (cin >> liczba) suma += liczba; catch(ios::failure & b) cout << b.what() << endl; cout << "Wyjatek we-wy - Koniec pliku? \n"; cout << "Ostatnia wpisana liczba= " << liczba << "\n"; cout << "Suma = " << suma << "\n"; return 0; 21

Wyjątek zostanie zgłoszony również wtedy, kiedy w danych wejściowych wystąpi błąd. Chcąc rozróżnić powód zgłoszenia wyjątku należałoby zbadać stan strumienia. Na przykład: #include <exception> int main() cout.precision(2); cout << showpoint << fixed; cout << "Wpisz liczby: "; double suma = 0.0; double liczba; try while (cin >> liczba) suma += liczba; if (!cin.eof()) throw ios::failure ("blad podczas czytania"); catch(ios::failure & b) cout << "Wyjatek we-wy: "<< b.what() << endl; return 1; cout << "Ostatnia wpisana liczba= " << liczba << "\n"; cout << "Suma = " << suma << "\n"; return 0; 22