OBSŁUGA
(język C++)
WYJĄTKÓW
Wykład 9.
Tomasz Marks - Wydział MiNI PW
-1-
Tomasz Marks - Wydział MiNI PW
-2-
Przeciwdziałanie błędom wykonania programu (1)
Przeciwdziałanie błędom wykonania programu (2)
double& Vector :: operator [ ] ( int i )
// vector.h
{
return V[ i ];
// operacja niebezpieczna !!!
struct xcp_memory { }; // zewnętrzna (wzgl. Vector) klasa pomocnicza
}
class Vector
double& Vector :: operator [ ] ( int i )
{
{
int Size, Nr;
if ( i >= 0 && i < Size ) return V[ i ];
double *V;
DisplayMessage( " komunikat o błę dzie" ); // fukcja systemowa lub własna static int DefSize, VCounter;
exit(1);
// funkcja standardowa
void Init ( int = 0, double = 0, double * = 0 );
}
public:
double& Vector :: operator [ ] ( int i )
Vector ( );
{
Vector ( int size, double val = 0 );
assert ( i >= 0 && i < Size ) // makro assert Vector ( int size, double arr[ ] );
return V[ i ];
……………………………….
}
struct xcp_scope // wewnętrzna (w Vector) klasa pomocnicza
{
double& Vector :: operator [ ] ( int i )
int index;
{
xcp_scope ( int );
if ( i < 0 || i >= Size ) throw xcp_scope( i ); // odrzucenie wyjątku
};
return V[ i ];
……………………………….
}
};
Tomasz Marks - Wydział MiNI PW
-3-
Tomasz Marks - Wydział MiNI PW
-4-
Przeciwdziałanie błędom wykonania programu (3)
Przeciwdziałanie błędom wykonania programu (4)
// vector.cpp
// metoda prywatna (wersja zmodyfikowana)
// implementacja konstruktora klasy xcp_scope
void Vector :: Init ( int s, double val, double tab[ ] )
Vector :: xcp_scope :: xcp_scope ( int i ) : index ( i ) { }
{
if ( VCounter > 7 ) throw "Za duzo wektorow!";
// metoda prywatna (wersja oryginalna)
Nr = ++VCounter;
if ( s < 0 ) throw "Blad konstrukcji!";
void Vector :: Init ( int s, double val, double tab[ ] )
if ( s <= 0 ) s = DefSize;
{
try
Nr = ++VCounter;
{
if ( s <= 0 ) s = DefSize;
V = new double [ s ];
V = new double [ s ];
}
if ( ! V ) { Size = 0; return; }
catch ( … ) { throw xcp_memory ( ); }
Size = s;
if ( ! V ) throw xcp_memory ( );
for ( s = 0; s < Size; ++s )
Size = s;
V [ s ] = tab ? tab [ s ] : val;
for ( s = 0; s < Size; ++s )
}
V [ s ] = tab ? tab [ s ] : val;
}
Tomasz Marks - Wydział MiNI PW
-5-
Tomasz Marks - Wydział MiNI PW
-6-
Przeciwdziałanie błędom wykonania programu (5)
Przeciwdziałanie błędom wykonania programu (6)
#include "vector.h"
void main ( )
Program wyś wietli:
{
Vector w( 4 );
for ( int i = -1; i < 7; i++ )
try {
i = -1
Blad konstrukcji!
cout << endl << "i = " << i << " "; i = 0
w[ 0 ] = 0
Vector z( i );
i = 1
w[ 1 ] = 1
w[ i ] = i;
i = 2
w[ 2 ] = 2
cout << "w[" << i << "] = " << w[ i ];
}
i = 3
w[ 3 ] = 3
catch ( const char * str )
i = 4
Odrzucono indeks 4
{ cout << str; }
i = 5
Odrzucono indeks 5
catch ( xcp_memory )
i = 6
Za duzo wektorow!
{ cout << "Brak pamieci"; }
catch ( Vector :: xcp_scope ex )
{ cout << "Odrzucono indeks " << ex.index; }
}
Tomasz Marks - Wydział MiNI PW
-7-
Tomasz Marks - Wydział MiNI PW
-8-
Obsługa wyjątków [2]
1.
Składnia instrukcji try-catch :
try // początek bloku try 2. Bloki catch są nazywane procedurami obsługi wyją tków.
{
…...............
……………
3. Każ da procedura obsługi wyją tków ma okreś lony typ sytuacji
……………
wyją tkowej, która ją aktywizuje.
} // koniec bloku try catch ( ….. ) { ……… } // 1. blok catch
catch ( ….. ) { ……… } // 2. blok catch
4. Zgłoszenie sytuacji wyją tkowej (throw ) podczas wykonywania
.…………………………..
bloku try skutkuje przekazaniem pewnego obiektu do odpowiedniej catch ( ….. ) { ……… } // ostatni blok catch
procedury obsługi.
blok try wraz ze wszystkimi nastę pują cymi po nim blokami catch stanowi składniowo jedną całość , którą nazywamy instukcją try-catch
5. Dopasowanie wyraż enia throw do procedury obsługi polega na porównaniu typu wartoś ci wyraż enia throw z typami ( parametrów ) okreś lonymi dla kolejnych procedur obsługi.
Tomasz Marks - Wydział MiNI PW
-9-
Tomasz Marks - Wydział MiNI PW
-10-
Obsługa wyjątków [3]
Obsługa wyjątków [4]
7. Jeż eli dopasowanie nie da pozytywnego rezultatu, t.zn. ż adna 6. Dopasowanie uznane bę dzie za pomyś lne, jeż eli spełniony zostanie procedura catch nie zostanie uruchomiona, to dana instrukcja jeden z trzech warunków:
try-catch bę dzie uznana za zakoń czoną i nastą pi sprawdzenie, czy w aktualnie wykonywanej funkcji jest blok try zawierają cy daną instukcję try-catch .
(i) oba typy są ś ciś le zgodne;
(ii) typ okreś lony dla parametru procedury obsługi jest
Jeż eli taki blok istnieje, to wykonana zostanie próba dopasowania publiczną klasą bazową klasy obiektu bę dą cego wartoś cią rozpatrywanygo wyją tku do wystę pują cych po nim procedur obsługi.
wyraż enia throw ;
Jeż eli taki blok nie istnieje, to wykonanie funkcji zostanie zakoń czone (iii) moż liwe jest standardowe przekształcenie typu wartoś ci i odpowiedni blok bę dzie poszukiwany w funkcji wywołują cej tę , wyraż enia throw , bę dą cej wskaź nikiem, na typ wskaź nikowy w której został odrzucony wyją tek itd. itd.
parametru procedury.
W skrajnym przypadku, jeż eli dopasowanie nie zostanie zrealizowane w obrę bie funkcji main() , obsługę wyją tku przejmie systemowa procedura Po pozytywnym rozpoznaniu jednego z warunków (i), (ii), (iii),
obsługi wyją tków, która wyś wietli odpowiedni komunikat i zakoń czy działanie programu.
dalsze frazy catch nie bę dą analizowane.
Tomasz Marks - Wydział MiNI PW
-11-
Tomasz Marks - Wydział MiNI PW
-12-
Obsługa wyjątków [6]
11. Deklaracja funkcji moż e zawierać klauzulę , która stanowi ograniczenie zbioru sytuacji wyją tkowych zgłaszanych bezpoś rednio 8. Zapis procedury obsługi nie musi zawierać nazwy przyjmowanego lub poś rednio przez tę funkcję :
obiektu (parametru). Nazwa jest konieczna jedynie w sytuacji, gdy
void fun1 ( …….. ); // może zgłaszać dowolne wyjątki
zachodzi rzeczywista potrzeba uż ycia wartoś ci tego parametru.
long fun2 ( …….. ) throw (const char*, xcp_scope&);
9. Trzykropek … pozwala okreś lić procedurę wychwytują cą wyją tki
// może zgłaszać wyjątki wymienionych typów
wszystkich typów.
double fun3 ( ….. ) throw ( );
// nie może zgłaszać żadnych wyjątków
10. Wewną trz procedury obsługi (catch ) moż na uż yć frazy throw Naruszenie zadeklarowanej specyfikacji spowoduje błą d wykonania z pustym wyraż eniem
programu.
throw;
Skutkuje to przekazaniem wyją tku do zewnę trznego bloku try .
12. Klauzula ograniczenia zbioru sytuacji wyją tkowych nie jest uważ ana W innych kontekstach uż ycie pustego wyraż enia throw jest błę dem.
za element okreś lenia typu funkcji ( tzn. nie wchodzi w skład
deskryptora funkcji ).
Tomasz Marks - Wydział MiNI PW
-13-
Tomasz Marks - Wydział MiNI PW
-14-
Funktory (1)
Definicja:
Funktorem (inaczej obiektem funkcyjnym) nazywamy obiekt
klasy, w której zdefiniowany został operator wywołania funkcji
FUNKTORY
t.zn. operator( ) .
Uwaga 1:
Korzystanie z obiektu funkcyjnego jest składniowo
identyczne z wywołaniem funkcji, dlatego wszę dzie tam,
gdzie parametrem funkcji ma być funkcja dostarczana jako
argument, moż na uż yć funktora.
Tomasz Marks - Wydział MiNI PW
-15-
Tomasz Marks - Wydział MiNI PW
-16-
Funktory (3)
Zwykłą funkcję
Uwaga 2:
typ funkcja ( argumenty )
{
Funktory są obiektami klas, w których oprócz operator()
Treść Funkcji
mogą być zdefiniowane inne metody i pola składowe, w tym
}
konstruktory. Zatem:
moż na "przekształcić " na funktor wg schematu:
- funktory mogą posiadać stan wewnę trzny, który moż e być class Funktor
inicjowany, odczytywany i modyfikowany;
{
public:
- moż e jednocześ nie istnieć wiele funktorów (obiektów) typ operator ( ) ( argumenty ) const
tej samej klasy (realizują cych takie same obliczenia),
{
ale ró
Tre
ż nią cych się stanem wewnę trznym;
ść Funkcji
}
}
- w przypadku zwykłych funkcji namiastką stanu moż e być funkcja;
zmienna globalna lub lokalna dla funkcji zm. statyczna.
Tomasz Marks - Wydział MiNI PW
-17-
Tomasz Marks - Wydział MiNI PW
-18-
Funktory (4)
Funktory – przykład (1)
#include <iostream>
Uwaga 3:
using namespace std;
class Sum
Każ dy funktor posiada własny typ, co moż e być
{
pomocne przy korzystaniu z szablonów:
int s;
public:
Sum ( ) : s(0) { }
- funktory bę dą c obiektami róż nych klas mają róż ne typy, void operator ( ) ( int a ) { s += a; }
nawet jeś li ich operatory wywołania funkcji mają identyczne int operator ( ) ( ) { return s; }
};
sygnatury;
int arr[ ] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, };
- zwykłe funkcje o identycznych sygnaturach są z punktu
void main ( )
widzenia szablonów nierozróż nialne.
{
Sum parzyste, nieparzyste;
for ( int i = 0; i < 10; ++i )
if ( i & 1 ) nieparzyste ( arr[ i ] ); else parzyste ( arr[ i ] ); cout << parzyste() << " " << nieparzyste();
// 25 30
}
Tomasz Marks - Wydział MiNI PW
-19-
Tomasz Marks - Wydział MiNI PW
-20-
#include <iostream>
using namespace std;
class Arr
{
int n, m;
double *A;
public:
Arr ( int i, int j ) : n( i ), m( j ) { A = new double [ n * m ]; }
KONWERSJE
~Arr ( ) { delete [ ] A; }
double & operator ( ) ( int i, int j ) { return A[ i * m + j ]; }
};
void main ( )
i KONWERTERY
{
Arr A (3, 4);
for ( int i = 0; i < 3; ++i )
for ( int j = 0; j < 4; ++j ) A( i, j ) = 10 * ( i + 1) + ( j + 1 ); for ( int i = 0; i < 3; ++i )
{
cout << endl;
for ( int j = 0; j < 4; ++j ) cout << A( i, j ) << " "; // 11 12 13 14
}
// 21 22 23 24
}
// 31 32 33 34
Tomasz Marks - Wydział MiNI PW
-21-
Tomasz Marks - Wydział MiNI PW
-22-
Klasyfikacja konwersji (klasycznych) (1)
Operatory konwersji (klasyczne)
1. Standardowe – mogą być wykonywane jawnie i niejawnie, np.
- dana typu arytmetycznego lub znakowego -> inna dana typu
Dla typu docelowego TYPE operator konwersji jest opisywany
arytmetycznego lub znakowego,
wyrażeniem postaci:
- dana reprezentowana literałem 0 lub 0L -> dana wskazująca dowolnego typu,
TYPE ( wyr ) , gdzie TYPE - identyfikator typu,
- dana dowolnego typu wskazującego -> dana typu void*,
wyr
- konwertowane wyrażenie,
- ………………………………..
albo
( TYPE ) wyr
, gdzie TYPE - dowolna nazwa typu,
2. Definiowalne – mogą być wykonywane jawnie i niejawnie:
wyr
- konwertowane wyrażenie,
- wszystkie konwersje określone przez konstruktory jednoparametrowe
i konwertery.
Np.
k = (int) 12.5;
// zapis poprawny
3. Dopuszczalne (predefiniowane konwersje, których skutek może zależeć od implementacji) – muszą być wykonywane jawnie, np.
k = int ( 12.5 );
// zapis poprawny
- dana całkowita -> dana wyliczeniowa,
- dana typu wskazującego -> dana innego typu wskazującego
p = (char *) z;
// zapis poprawny
(z wyłączeniem przypadku void*),
- dana typu całkowitego <-> dana typu wskazującego,
p = char* ( z );
// BŁĄD! - char* jest nazwą typu
- wskazanie zmiennej ustalonej (albo ulotnej) -> wskazanie analogicznej
// ale nie jest identyfikatorem
zmiennej nieustalonej (albo nieulotnej),
- ………………………………..
Tomasz Marks - Wydział MiNI PW
-23-
Tomasz Marks - Wydział MiNI PW
-24-
Klasyfikacja konwersji (klasycznych) (2)
Klasyfikacja konwersji (klasycznych) (3)
2. Konwersje odnośnikowe (referencyjne)
1. Konwersje wartościowe (nieodnośnikowe, niereferencyjne)
Jeżeli typ docelowy jest odnośnikowy, to rezultatem konwersji będzie
Jeżeli typ docelowy nie jest odnośnikowy, to rezultatem konwersji
odnośnik typu TYPE zainicjowany argumentem konwersji.
będzie zmienna typu TYPE zainicjowana daną powstałą z przekształcenia Taki odnośnik jest L-nazwą tylko wówczas, gdy spełnione są 2 warunki:
danej o wartości wyr na daną typu TYPE. Zmienna taka nie jest L-nazwą.
(i) wyr jest L-nazwą zmiennej,
(ii) istnieje konwersja & wyr na TYPE*
(konwersja wskazania argumentu na wskazanie typu docelowego).
Np.
double X = 1.5;
Np.
//(int) X = 12;
// BŁĄD! - (int) X nie jest L-nazwą
const int X = 10;
cout << (int) X;
// O.K. - wyprowadzi 1
X = 12
// BŁĄD! - X ma atrybut const
// rezultatem konwersji jest zmienna tymczasowa,
(int &) X = 20;
// O.K.
int & tmp = *(int *) & X;
// którą można zdefiniować instrukcją: int tmp = 1;
// (zmienna tmp skojarzona jest z obszarem
// pamięci przydzielonym na zmienną X)
cout << X;
// wyprowadzi 20 albo 10 (!!!)
.
Tomasz Marks - Wydział MiNI PW
-25-
Tomasz Marks - Wydział MiNI PW
-26-
Konwertery (1) - Definicja
Konwertery (2) – przykład definiowania
class CMPLX
{
Konwerterem definiowanego typu Class do typu docelowego TYPE
double Re, Im;
public:
jest bezparametrowa metoda o deklaracji
CMPLX ( double ); // konwersja double CMPLX
Class :: operator TYPE ( );
……………………..
w której TYPE jest nazwą (!) typu nie zawierającą nawiasów.
operator double ( ); // konwersja CMPLX double
operator const char * ( ); // konwersja CMPLX const char *
Wykonanie w ciele konwertera instrukcji
……………………..
return wyr ;
};
skutkuje udostępnienien zmiennej typu TYPE o wartości
(TYPE) wyr
CMPLX :: CMPLX ( double re ) : Re(re), Im(0) { }
CMPLX :: operator double ( ) { return Re; }
UWAGA.
Wymaga się, by konwersja typu wyrażenia wyr do TYPE istniała.
CMPLX :: operator const char * ( )
{
if ( Re == 0 && Im == 0 ) return "Zero";
if ( Im == 0 ) return "Real";
if ( Re == 0 ) return "Imag";
return "Cmplx";
}
Tomasz Marks - Wydział MiNI PW
-27-
Tomasz Marks - Wydział MiNI PW
-28-
Konwertery (3) – przykład użycia
"Nowe" operatory konwersji (1)
#include "cmplx.h"
typedef const char * ConstCharPtr;
Obecnie zaleca się stosowanie "nowych" operatorów konwersji,
void main ( )
zapisywanych z wykorzystaniem słów kluczowych:
{
CMPLX X(1.5,2.3), Y(5), Z;
double a, b, c;
static_cast
const char *A, *B, *C;
reinterpret_cast
a = (double) X; // jawne użycie konwertera na double
const_cast
b = double (X); // jawne użycie konwertera na double
dynamic_cast
c = X; // niejawne użycie konwertera na double
cout << a << b << c; // 1.5 1.5 1.5
Użycie "nowych" operatorów konwersji ma postać:
A = (const char *) Y; // jawne użycie konwertera na const char *
//A = const char * (Y); // BŁĄD! - const char * nie jest identyfikatorem B = ConstCharPtr (Y); // jawne użycie konwertera na const char *
xxxxx_cast < typ_docelowy > ( konwertowane_wyraż enie ) C = Y; // niejawne użycie konwertera na const char *
cout << A << B << C; // Real Real Real
X = (CMPLX) a; // jawne użycie konwersji konstruktorowej
Y = CMPLX (a); // jawne użycie konwersji konstruktorowej
Z = a; // niejawne użycie konwersji konstruktorowej
cout << X << Y << Z; // [1.5,0] [1.5,0] [1.5,0]
}
Tomasz Marks - Wydział MiNI PW
-29-
Tomasz Marks - Wydział MiNI PW
-30-
"Nowe" operatory konwersji (2)
"Nowe" operatory konwersji (3)
static_cast – wykorzystywany do konwersji typów spokrewnionych
const_cast – wykorzystywany do usuwania kwalifikatora const
- typu zmiennopozycyjny -> typu całkowity,
Np.
- typ wyliczeniowy -> typ całkowity,
const int x = 10;
- typ wskaźnikowy -> inny spokrewniony typ wskaźnikowy, ……
const_cast <int&> ( x ) = 20;
Np.
int x = static_cast<int>(22.6);
PhVectorB *p = static_cast<PhVectorB*>(Vec);
dynamic_cast – wykorzystywany do konwersji kontrolowanej w czasie
int *p = static_cast<int*>(malloc(20));
wykonywania programu.
Np.
reinterpret_cast – wykorzystywany do konwersji typów niespokrewnionych Zwierz* pZ;
- typ całkowity -> typ wskaźnikowy,
Pies P("Burek");
- typ wskaźnikowy -> inny niespokrewniony typ wskaźnikowy, ……
Ssak D("Umo");
Np.
pZ = dynamic_cast <Zwierz*> ( &P ); // pZ = wskazanie na P
io_Unit* we = reinterpret_cast<io_Unit*>(0x123e);
pZ = dynamic_cast <Zwierz*> ( &D ); // pZ = 0, jeśli niepoprawne Zwierz& rZ = dynamic_cast <Zwierz&> ( *pZ );
// odrzuci wyjątek bad_cast, jeśli niepoprawne
Tomasz Marks - Wydział MiNI PW
-31-
Tomasz Marks - Wydział MiNI PW
-32-
Tomasz Marks - Wydział MiNI PW
-33-