Professional Documents
Culture Documents
czym s wyjtki,
Wyjtki
Sytuacji wyjtkowych nie da si wyeliminowa; mona si jedynie na nie przygotowa.
Uytkownikom programw od czasu do czasu koczy si pami i jedynym zagadnieniem
pozostaje to, co twj program zrobi w takim przypadku. Masz wtedy do wyboru:
zaamanie programu,
Cho nie zawsze jest konieczne (a czasem nawet niewskazane) automatyczne i niewidoczne
obsugiwanie wszystkich wyjtkowych sytuacji, jednak trzeba co zrobi, aby nie pozwoli na
zaamanie si programu.
Obsuga wyjtkw w C++ dostarcza bezpiecznej (ze wzgldu na typ), zintegrowanej metody
reagowania na niezwyke, cho przewidywalne sytuacje, ktre pojawiaj si podczas dziaania
programu.
Wyjtki
W C++ wyjtek jest obiektem, ktry jest przekazywany z obszaru kodu, w ktrym wystpi
problem, do tej czci kodu, ktra odpowiada za jego obsuenie. Typ wyjtku okrela obszar
kodu, ktry ma obsuy problem, za zawarto zgoszonego obiektu, o ile istnieje, moe
posuy do dokadniejszego poinformowania uytkownika.
Reguy rzdzce wyjtkami s bardzo proste:
rzeczywista alokacja zasobw (na przykad alokacja pamici czy blokowanie pliku) zwykle
odbywa si na bardzo niskim poziomie programu,
logika okrelajca, co naley zrobi, gdy operacja si nie powiedzie, pami nie moe zosta
zaalokowana czy plik nie moe zosta zablokowany, zwykle znajduje si na duo wyszym
poziomie programu, wraz z kodem wsppracujcym z uytkownikiem,
Z kolei bloki catch (wychwy) obsuguj wyjtki zgoszone w bloku try. Na przykad:
try
{
SomeDangerousFunction();
}
catch(OutOfMemory)
{
// podejmij jakie dziaania przeciwdziaajce brakowi pamici
}
catch(FileNotFound)
{
// podejmij czynnoci przeciwdziaajce brakowi pliku na dysku
}
UWAGA Niektre bardzo stare kompilatory nie obsuguj wyjtkw. Wyjtki s jednak czci
standardu ANSI C++ i wszystkie najnowsze wersje kompilatorw w peni je obsuguj. Jeli
posiadasz starszy kompilator, nie bdziesz mg skompilowa i uruchomi przykadw
zawartych w tym rozdziale. Jednak mimo to powiniene przeczyta ca jego zawarto i wrci
do tego materiau pniej, gdy zdobdziesz nowsz wersj kompilatora.
#include <iostream>
using namespace std;
const int DefaultSize = 10;
class Array
{
public:
// konstruktory
Array(int itsSize = DefaultSize);
Array(const Array &rhs);
~Array() { delete [] pType;}
// operatory
Array& operator=(const Array&);
int& operator[](int offSet);
const int& operator[](int offSet) const;
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
// akcesory
int GetitsSize() const { return itsSize; }
// funkcja zaprzyjaniona
friend ostream& operator<< (ostream&, const Array&);
class xBoundary {};
private:
int *pType;
int itsSize;
};
Array::Array(int size):
itsSize(size)
{
pType = new int[size];
for (int i = 0; i<size; i++)
pType[i] = 0;
}
Array& Array::operator=(const Array &rhs)
{
if (this == &rhs)
return *this;
delete [] pType;
itsSize = rhs.GetitsSize();
pType = new int[itsSize];
for (int i = 0; i<itsSize; i++)
pType[i] = rhs[i];
return *this;
}
Array::Array(const Array &rhs)
{
itsSize = rhs.GetitsSize();
pType = new int[itsSize];
for (int i = 0; i<itsSize; i++)
pType[i] = rhs[i];
}
int& Array::operator[](int offSet)
{
int size = GetitsSize();
if (offSet >= 0 && offSet < GetitsSize())
return pType[offSet];
throw xBoundary();
return pType[0]; // ucisza MSC
}
const int& Array::operator[](int offSet) const
{
int mysize = GetitsSize();
if (offSet >= 0 && offSet < GetitsSize())
return pType[offSet];
throw xBoundary();
return pType[0]; // ucisza MSC
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
}
ostream& operator<< (ostream& output, const Array& theArray)
{
for (int i = 0; i<theArray.GetitsSize(); i++)
output << "[" << i << "] " << theArray[i] << endl;
return output;
}
int main()
{
Array intArray(20);
try
{
for (int j = 0; j< 100; j++)
{
intArray[j] = j;
cout << "intArray[" << j << "] w porzadku..." << endl;
}
}
catch (Array::xBoundary)
{
cout << "Nie moglem przetworzyc tych danych!\n";
}
cout << "Gotowe.\n";
return 0;
}
Wynik
intArray[0] w porzadku...
intArray[1] w porzadku...
intArray[2] w porzadku...
intArray[3] w porzadku...
intArray[4] w porzadku...
intArray[5] w porzadku...
intArray[6] w porzadku...
intArray[7] w porzadku...
intArray[8] w porzadku...
intArray[9] w porzadku...
intArray[10] w porzadku...
intArray[11] w porzadku...
intArray[12] w porzadku...
intArray[13] w porzadku...
intArray[14] w porzadku...
intArray[15] w porzadku...
intArray[16] w porzadku...
intArray[17] w porzadku...
intArray[18] w porzadku...
intArray[19] w porzadku...
Nie moglem przetworzyc tych danych!
Gotowe.
Analiza
Listing 20.1 przedstawia nieco okrojon klas Array, opart na wzorcuszablonie opracowanym w
rozdziale 19., WzorceSzablony.
W linii 24., wewntrz deklaracji zewntrznej klasy Array zostaje zadeklarowana nowa klasa,
xBoundary.
Ta nowa klasa w aden sposb nie jest wyrniana jako klasa wyjtku. Jest po prostu tak sam
klas, jak kada inna. Jest ona bardzo prosta; nie zawiera adnych danych ani funkcji skadowych.
Mimo to ,jest jednak najzupeniej poprawn klas.
W rzeczywistoci, stwierdzenie, e nie posiada ona adnych metod, jest bdne, gdy kompilator
automatycznie przypisuje jej konstruktor domylny, destruktor, konstruktor kopiujcyi oraz
operator przypisania. , wic wNaprawd klasa ta posiada cztery funkcje, przy braku jakichkolwiek
wasnych danych.
Zwr uwag, e zadeklarowanie jej wewntrz klasy Array suy jedynie do wzajemnego
powizania tych klas. Jak mwilimy w rozdziale 16., Dziedziczenie zaawansowane, ani klasa
Array nie ma specjalnych uprawnie dostpu do klasy xBoundary, ani klasa xBoundary nie ma
preferencyjnego dostpu do skadowych klasy Array.
W liniach od 62. do 69. oraz od 72. do 79. operatory indeksu zostay zmodyfikowane tak, by
sprawdzay zadany indeks i, gdy znajduje si on poza zakresem, zgaszay klas xBoundary
jako wyjtek. Nawiasy s wymagane dla odrnienia tego wywoania konstruktora klasy
xBoundary od uycia staej wyliczeniowej. Zauwa, e niektre kompilatory Microsoftu
wymagaj dostarczenia instrukcji return, adekwatnej do deklaracji funkcji (w tym przypadku
referencji do typu int), mimo, i gdy w linii 67. zostanie zgoszony wyjtek, to wykonanie ten
kodu nigdy nie dotrze do linii 68. Jest to bd kompilatora, wskazujcy e nawet Microsoft mia
problemy z tym zagadnieniem!
W linii 91. sowo kluczowe try rozpoczyna blok try, ktry koczy si na linii 98. Wewntrz tego
bloku, do tablicy zadeklarowanej w linii 90. zostaje dodanych dwadzieciawpisanych 101
elementw.
W linii 99. rozpoczyna si blok catch, wychwytujcy wyjtki typu xBoundary.
W programie sterujcym, w liniach od 88. do 105, wystpuje blok try, w ktrym jest
inicjalizowany kady element tablicy. Gdy zmienna j (linia 93.) dojdzie do wartoci 20, nastpuje
odwoanie do elementu tablicy o indeksie 20. To powoduje, e test przeprowadzany w linii 65.6
nie udaje si i w linii 67. operator[] zgasza wyjtek xBoundary.
Sterowanie programem przechodzi do bloku catch, zaczynajcego si w linii 99., w ktrym
wyjtek zostaje obsuony (w tym przypadku przez wypisanie komunikatu bdu). Wykonanie
programu przechodzi przez koniec bloku catch w linii 102.
Bloki try
Blok try jest seri instrukcji, zaczynajc si od sowa kluczowego try, po ktrym nastpuje
nawias klamrowy otwierajcy. Blok koczy si nawiasem klamrowym zamykajcym.
Przykad
try
{
Funkcja();
};
Bloki catch
Blok catch jest seri instrukcji, z ktrych kada zaczyna si od sowa kluczowego catch, po
ktrym nastpuje ujty w nawiasy okrge typ wyjtku oraz nawias klamrowy otwierajcy i
zamykajcy.
Przykad
try
{
Funkcja();
};
catch (OutOfMemory)
{
// obsuga braku pamici
}
Wychwytywanie wyjtkw
Gdy zostaje zgoszony wyjtek, sprawdzany jest stos wywoa. Stos wywoa jest list wywoa
funkcji tworzon w momencie, gdy ktra z czci programu wywouje t funkcj.
Stos wywoa ledzi ciek wykonania. Jeli funkcja main() wywouje funkcj
Animal::GetFavoriteFood(), a funkcja GetFavoriteFood() wywouje funkcj
Animal::LookupPreferences(), ktra z kolei wywouje fstream::operator>>(),
wszystkie te wywoania znajd si na stosie. Funkcja wywoywana rekurencyjnie moe wystpi
na stosie wielokrotnie.
Wyjtek jest przekazywany w gr stosu, do kadego obejmujcego bloku. Nazywa si to
rozwijaniem stosu. Gdy stos jest rozwijany, wywoywane s destruktory lokalnych obiektw na
stosie i obiekty te s niszczone.
Po kadym bloku try wystpuje jedna lub wicej instrukcji catch. Gdy wyjtek pasuje do jednej
z instrukcji catch, zakada si, e zostaje on obsuony przez wykonanie tej instrukcji. Jeli nie
pasuje do adnej instrukcji catch, rozwijanie stosu przebiega dalej.
Gdy wyjtek przebdzie ca drog a do pocztku programu (funkcji main()) i wci nie jest
wychwycony, jest wywoywana wbudowana procedura obsugi, ktra koczy dziaanie programu.
Naley zdawa sobie spraw, e rozwijanie stosu jest dziaaniem jednokierunkowym. W miar
rozwijania stosu, zawarte na nim obiekty s niszczone. Nie ma wic powrotu: gdy wyjtek
zostanie obsuony, program kontynuuje dziaanie po bloku try tej instrukcji catch, ktra
obsuya wyjtek.
Na listingu 20.1 wykonanie programu zostanie wznowione od linii 101., czyli pierwszej linii po
bloku try instrukcji catch, ktra obsuya wyjtek xBoundary. Pamitaj, e po zgoszeniu
wyjtku dziaanie programu jest kontynuowane za blokiem catch, a nie w punkcie, w ktrym
zosta zgoszony wyjtek.
#include <iostream>
using namespace std;
const int DefaultSize = 10;
class Array
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
{
public:
// konstruktory
Array(int itsSize = DefaultSize);
Array(const Array &rhs);
~Array() { delete [] pType;}
// operatory
Array& operator=(const Array&);
int& operator[](int offSet);
const int& operator[](int offSet) const;
// akcesory
int GetitsSize() const { return itsSize; }
// funkcja zaprzyjaniona
friend ostream& operator<< (ostream&, const Array&);
// definiujemy klasy wyjtkw
class xBoundary {};
class xTooBig {};
class xTooSmall{};
class xZero {};
class xNegative {};
private:
int *pType;
int itsSize;
};
int& Array::operator[](int offSet)
{
int size = GetitsSize();
if (offSet >= 0 && offSet < GetitsSize())
return pType[offSet];
throw xBoundary();
return pType[0]; // ucisza MSC
}
const int& Array::operator[](int offSet) const
{
int mysize = GetitsSize();
if (offSet >= 0 && offSet < GetitsSize())
return pType[offSet];
throw xBoundary();
return pType[0];
// ucisza MSC
}
Array::Array(int size):
itsSize(size)
{
if (size == 0)
throw xZero();
if (size < 10)
throw xTooSmall();
if (size > 30000)
throw xTooBig();
if (size < 1)
throw xNegative();
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
Wynik
Poprosiles o tablice zawierajaca zero obiektow!
Gotowe.
Analiza
W liniach od 25. do 29. zostay utworzone cztery nowe klasy: xTooBig, xTooSmall, xZero oraz
xNegative. W konstruktorze, zawartym w liniach do 56. do 71., sprawdzany jest rozmiar
przekazywany jako parametr tego konstruktora. Gdy jest zbyt duy, zbyt may, ujemny lub zerowy,
zostaje zgoszony wyjtek.
Blok try zosta zmodyfikowany i zawiera teraz instrukcje catch se dla kadego warunku
innego ni rozmiar ujemny. Warunek ten zostaje wychwycony (rozpoznany) przez wychwytujc
wszystko instrukcj catch(...), zawart w linii 101.
Wyprbuj dziaanie tego programu, stosujc rne wartoci rozmiaru tablicy. Nastpnie sprbuj
zastosowa rozmiar wynoszcy 5. Moge oczekiwa, e zostanie zgoszony wyjtek
xNegative, ale uniemoliwia to kolejno testw przeprowadzanych w konstruktorze: size <
10 jest obliczane wczeniej ni size < 1. Aby to poprawi, zamie miejscami linie 61. i 62. z
liniami 65. i 66., po czym ponownie skompiluj program.
Hierarchie wyjtkw
Wyjtki s klasami, wic mona z nich wyprowadza klasy pochodne. By moe przydatne byoby
stworzenie klasy xSize i wyprowadzenie z niej klas xZero, xTooSmall, xTooBig oraz
xNegative. Niektre funkcje mogyby wtedy wyapywa po prostu wyjtki xSize, a inne
mogyby wyapywa bardziej specyficzne typy bdw. Listing 20.3 ilustruje ten pomys.
Listing 20.3. Hierarchie klas i wyjtki
0:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
#include <iostream>
using namespace std;
const int DefaultSize = 10;
class Array
{
public:
// konstruktory
Array(int itsSize = DefaultSize);
Array(const Array &rhs);
~Array() { delete [] pType;}
// operatory
Array& operator=(const Array&);
int& operator[](int offSet);
const int& operator[](int offSet) const;
// akcesory
int GetitsSize() const { return itsSize; }
// funkcja zaprzyjaniona
friend ostream& operator<< (ostream&, const Array&);
// definiujemy klasy wyjtkw
class xBoundary {};
class xSize {};
class xTooBig : public xSize {};
class xTooSmall : public xSize {};
class xZero : public xTooSmall {};
class xNegative : public xSize {};
private:
int *pType;
int itsSize;
};
Array::Array(int size):
itsSize(size)
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
{
if (size
throw
if (size
throw
if (size
throw
if (size
throw
== 0)
xZero();
> 30000)
xTooBig();
<1)
xNegative();
< 10)
xTooSmall();
// ucisza MSC
}
int main()
{
try
{
Array intArray(0);
for (int j = 0; j< 100; j++)
{
intArray[j] = j;
cout << "intArray[" << j << "] w porzadku...\n";
}
}
catch (Array::xBoundary)
{
cout << "Nie moglem przetworzyc tych danych!\n";
}
catch (Array::xTooBig)
{
cout << "Ta tablica jest zbyt duza...\n";
}
catch (Array::xTooSmall)
{
cout << "Ta tablica jest zbyt mala...\n";
}
catch (Array::xZero)
{
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
Wynik
Ta tablica jest zbyt mala...
Gotowe.
Analiza
Jedyne znaczce zmiany pojawiaj si w liniach od 27. do 30., gdzie ustanawiana jest hierarchia
klas. Klasy xTooBig, xTooSmall oraz xNegative s wyprowadzone z klasy xSize, za klasa
xZero jest wyprowadzona z klasy xTooSmall.
Stworzona zostaa tablica Array o zerowym rozmiarze, ale co si stao? Wyglda na to, e zosta
wychwycony niewaciwy wyjtek! Sprawd dokadnie blok catch okae si, e wyjtek typu
xTooSmall jest wychwytywany przed wyjtkiem typu xZero. Poniewa zgaszany jest wyjtek
typu xZero, a obiekt klasy xZero jest obiektem klasy xTooSmall, zostaje on wychwycony przez
blok catch wyjtku typu xTooSmall. Po obsueniu, wyjtek nie jest przekazywany dalej, do
kolejnych blokw wychwytywania, dlatego nigdy nie zostaje wywoany blok catch dla wyjtku
typu xZero.
Rozwizaniem tego problemu jest uwany dobr kolejnoci blokw catch (tak, aby najbardziej
specyficzne wyjtki byy wychwytywane w pierwszej kolejnoci, a mniej specyficzne w dalszej).
W tym konkretnym przykadzie problem ten mona rozwiza, zamieniajc miejscami bloki
catch dla wyjtkw xZero i xTooSmall.
#include <iostream>
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
int itsSize;
63: };
64:
65:
66: Array::Array(int size):
67: itsSize(size)
68: {
69:
if (size == 0)
70:
throw xZero(size);
71:
if (size > 30000)
72:
throw xTooBig(size);
73:
if (size <1)
74:
throw xNegative(size);
75:
if (size < 10)
76:
throw xTooSmall(size);
77:
78:
pType = new int[size];
79:
for (int i = 0; i<size; i++)
80:
pType[i] = 0;
81: }
82:
83:
84: int& Array::operator[] (int offSet)
85: {
86:
int size = GetitsSize();
87:
if (offSet >= 0 && offSet < GetitsSize())
88:
return pType[offSet];
89:
throw xBoundary();
90:
return pType[0];
91: }
92:
93: const int& Array::operator[] (int offSet) const
94: {
95:
int size = GetitsSize();
96:
if (offSet >= 0 && offSet < GetitsSize())
97:
return pType[offSet];
98:
throw xBoundary();
99:
return pType[0];
100: }
101:
102: int main()
103: {
104:
try
105:
{
106:
Array intArray(9);
107:
for (int j = 0; j< 100; j++)
108:
{
109:
intArray[j] = j;
110:
cout << "intArray[" << j << "] w porzadku..." << endl;
111:
}
112:
}
113:
catch (Array::xBoundary)
114:
{
115:
cout << "Nie moglem przetworzyc tych danych!\n";
116:
}
117:
catch (Array::xZero theException)
118:
{
119:
cout << "Poprosiles o tablice Array zawierajaca zero
elementow" << endl;
120:
cout << "Otrzymano " << theException.GetSize() << endl;
121:
}
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
Wynik
Ta tablica jest zbyt mala...
Otrzymano 9
Gotowe.
Analiza
Deklaracja klasy xSize zawiera teraz w linii 33. zmienn skadow itsSize, a w linii 31.
funkcj skadow GetSize(). Oprcz tego, zosta dla niej stworzony konstruktor przyjmujcy
warto cakowit i inicjalizujcy t zmienn skadow, co zostao pokazane w linii 29.
Klasy pochodne deklaruj konstruktory, ktre tylko inicjalizuj klas bazow. Nie zostay
zadeklarowane adne inne funkcje (midzy innymi po to, by skrci objto listingu).
Instrukcje catch w liniach od 113. do 135. posiadaj teraz nazwany obiekt xException
wychwytywanego wyjtku i uywaj go w celu uzyskania dostpu do danej zawartej w jego
zmiennej skadowej itsSize.
UWAGA Pamitaj o tym, e obiekt wyjtku jest tworzony w wyniku wystpienia wyjtkowej
sytuacji, wic powiniene uwaa, eby nie spowodowa takiej samej sytuacji w konstruktorze.
Gdy tworzysz wyjtek braku pamici (OutOfMemory), nie powiniene alokowa pamici w
konstruktorze.
0:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
#include <iostream>
using namespace std;
const int DefaultSize = 10;
class Array
{
public:
// konstruktory
Array(int itsSize = DefaultSize);
Array(const Array &rhs);
~Array() { delete [] pType;}
// operatory
Array& operator=(const Array&);
int& operator[](int offSet);
const int& operator[](int offSet) const;
// akcesory
int GetitsSize() const { return itsSize; }
// funkcja zaprzyjaniona
friend ostream& operator<<
(ostream&, const Array&);
// definiujemy klasy wyjtkw
class xBoundary {};
class xSize
{
public:
xSize(int size):itsSize(size) {}
~xSize(){}
virtual int GetSize() { return itsSize; }
virtual void PrintError()
{
cout << "Blad rozmiaru. Otrzymano: ";
cout << itsSize << endl;
}
protected:
int itsSize;
};
class xTooBig : public xSize
{
public:
xTooBig(int size):xSize(size){}
virtual void PrintError()
{
cout << "Zbyt duza! Otrzymano: ";
cout << xSize::itsSize << endl;
}
};
class xTooSmall : public xSize
{
public:
xTooSmall(int size):xSize(size){}
virtual void PrintError()
{
cout << "Zbyt mala! Otrzymano: ";
cout << xSize::itsSize << endl;
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
}
};
class xZero : public xTooSmall
{
public:
xZero(int size):xTooSmall(size){}
virtual void PrintError()
{
cout << "Zerowa!!. Otrzymano: " ;
cout << xSize::itsSize << endl;
}
};
class xNegative : public xSize
{
public:
xNegative(int size):xSize(size){}
virtual void PrintError()
{
cout << "Ujemna! Otrzymano: ";
cout << xSize::itsSize << endl;
}
};
private:
int *pType;
int itsSize;
};
Array::Array(int size):
itsSize(size)
{
if (size == 0)
throw xZero(size);
if (size > 30000)
throw xTooBig(size);
if (size <1)
throw xNegative(size);
if (size < 10)
throw xTooSmall(size);
pType = new int[size];
for (int i = 0; i<size; i++)
pType[i] = 0;
}
int& Array::operator[] (int offSet)
{
int size = GetitsSize();
if (offSet >= 0 && offSet < GetitsSize())
return pType[offSet];
throw xBoundary();
return pType[0];
}
const int& Array::operator[] (int offSet) const
{
int size = GetitsSize();
if (offSet >= 0 && offSet < GetitsSize())
return pType[offSet];
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
throw xBoundary();
return pType[0];
}
int main()
{
try
{
Array intArray(9);
for (int j = 0; j< 100; j++)
{
intArray[j] = j;
cout << "intArray[" << j << "] w porzadku...\n";
}
}
catch (Array::xBoundary)
{
cout << "Nie moglem przetworzyc tych danych!\n";
}
catch (Array::xSize& theException)
{
theException.PrintError();
}
catch (...)
{
cout << "Cos poszlo nie tak!\n";
}
cout << "Gotowe.\n";
return 0;
}
Wynik
Zbyt mala! Otrzymano: 9
Gotowe.
Analiza
Listing 20.5 deklaruje w klasie xSize wirtualn metod o nazwie PrintError(). Ta metoda
wypisuje komunikat bdu oraz aktualny rozmiar klasy. Jest przesonita w kadej z klas
pochodnych.
W linii 141. obiekt wyjtku jest deklarowany jako referencja. Gdy zostaje wywoana funkcja
PrintError() dla referencji do obiektu, polimorfizm powoduje, e zostaje wywoana metoda
waciwej klasy. Dziki temu kod jest bardziej przejrzysty, atwiejszy do zrozumienia i duo
atwiejszy w konserwacji.
Wyjtki i wzorce
Tworzc wspdziaajce z wzorcamiwzorcami wyjtki, masz do wyboru: tworzenie wyjtku dla
kadego egzemplarza wzorcawzorca lub uycie klas wyjtkw zadeklarowanych poza deklaracj
wzorcaszablonu. Listing 20.6 ilustruje obie moliwoci.
Listing 20.6. Uycie wyjtkw z wzorcami
0:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
#include <iostream>
using namespace std;
const int DefaultSize = 10;
class xBoundary {};
template <class T>
class Array
{
public:
// konstruktory
Array(int itsSize = DefaultSize);
Array(const Array &rhs);
~Array() { delete [] pType;}
// operatory
Array& operator=(const Array<T>&);
T& operator[](int offSet);
const T& operator[](int offSet) const;
// akcesory
int GetitsSize() const { return itsSize; }
// funkcja zaprzyjaniona
friend ostream& operator<< (ostream&, const Array<T>&);
// definiujemy klasy wyjtkw
class xSize {};
private:
int *pType;
int itsSize;
};
template <class T>
Array<T>::Array(int size):
itsSize(size)
{
if (size <10 || size > 30000)
throw xSize();
pType = new T[size];
for (int i = 0; i<size; i++)
pType[i] = 0;
}
template <class T>
Array<T>& Array<T>::operator=(const Array<T> &rhs)
{
if (this == &rhs)
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
return *this;
delete [] pType;
itsSize = rhs.GetitsSize();
pType = new T[itsSize];
for (int i = 0; i<itsSize; i++)
pType[i] = rhs[i];
}
template <class T>
Array<T>::Array(const Array<T> &rhs)
{
itsSize = rhs.GetitsSize();
pType = new T[itsSize];
for (int i = 0; i<itsSize; i++)
pType[i] = rhs[i];
}
template <class T>
T& Array<T>::operator[](int offSet)
{
int size = GetitsSize();
if (offSet >= 0 && offSet < GetitsSize())
return pType[offSet];
throw xBoundary();
return pType[0];
}
template <class T>
const T& Array<T>::operator[](int offSet) const
{
int mysize = GetitsSize();
if (offSet >= 0 && offSet < GetitsSize())
return pType[offSet];
throw xBoundary();
}
template <class T>
ostream& operator<< (ostream& output, const Array<T>& theArray)
{
for (int i = 0; i<theArray.GetitsSize(); i++)
output << "[" << i << "] " << theArray[i] << endl;
return output;
}
int main()
{
try
{
Array<int> intArray(9);
for (int j = 0; j< 100; j++)
{
intArray[j] = j;
cout << "intArray[" << j << "] w porzadku..." << endl;
}
}
catch (xBoundary)
{
cout << "Nie moglem przetworzyc tych danych!\n";
}
catch (Array<int>::xSize)
{
111:
112:
113:
114:
115:
116:
Wynik
Zly rozmiar!
Gotowe.
Analiza
Pierwszy wyjtek, xBoundary, jest zadeklarowany w linii 4., poza definicj wzorcaszablonu.
Drugi wyjtek, xSize, jest zadeklarowany w linii 28., wewntrz definicji wzorcaszablonu.
Wyjtek xBoundary nie jest powizany z klas wzorcaszablonu, ale moe by uywany tak samo,
jak inne klasy. Wyjtek xSize jest zwizany z wzorcemszablonem i musi by wywoywany w
oparciu o egzemplarz klasy Array. Rnic wida w skadni dwch instrukcji catch. Linia 105.
zawiera instrukcj catch(xBoundary), a linia 109. zawiera instrukcj
catch(Array<int>::xSize). Ta druga instrukcja jest powizana z klas Array przechowujc
wartoci typu int.
Taka dyskusja czsto przeradza si w zacity spr, ale najbardziej sensownym sposobem podjcia
decyzji jest zadanie sobie nastpujcych pyta: czy uycie wyjtkw uatwi, czy utrudni
zrozumienie kodu? Czy ryzyko powstania bdw i wyciekw pamici zwikszy si, czy
zmniejszy? Czy utrzymaniemodernizacja kodu bdzie atwiejszae, czy trudniejszae? Te decyzje,
podobnie jak wiele innych, wymagaj analizy wad i zalet; nie ma jednej, waciwej odpowiedzi.
UWAGA Psucie si kodu to art programistw, z ktrego pynie wana lekcja. Programy s
bardzo zoone: pluskwy, bdy i pomyki mog si ukrywa przez duszy czas. Chro sam
siebie, piszc atwy w utrzymaniu kod.
Pluskwy i odpluskwianie
Prawie wszystkie nowoczesne rodowiska programowania zawieraj jeden lub wicej
wysokowydajnych debuggerw. Podstawowa zasada korzystania z debuggera (nazwa pochodzi od
sowa debug, odpluskwia) jest nastpujca: uruchamiasz debugger, ktry aduje kod rdowy
programu, po czym uruchamiasz swj program w debuggerze. Dziki temu moesz ledzi
wykonanie kadej instrukcji w programie oraz sprawdza wartoci zmiennych, ktre zmieniaj si
podczas jego dziaania.
Wszystkie kompilatory umoliwiaj kompilacj z symbolami lub bez. Kompilowanie z symbolami
informuje kompilator, by tworzy konieczne odwzorowanie pomidzy kodem rdowym
programu a generowanym kodem wykonywalnym; debugger uywa tego odwzorowania do
wskazania linii kodu rdowego, ktra odpowiada nastpnemu dziaaniu w programie.
Punkty wstrzymania
Punkty wstrzymania (ang. breakpoint) s instrukcj dla debuggera nakazujc mu, by wstrzyma
wykonanie programu w momencie dotarcia do okrelonej linii kodu. Dziki temu moesz
uruchomi program tak, aby dziaa normalnie a do chwili dotarcia do obszaru kodu, ktry ci
interesuje. Punkty wstrzymania pomagaj w analizowaniu biecych wartoci zmiennych tuz
przed wykonaniem krytycznych linii kodu.
Sprawdzanie pamici
Czasem uytkownik musi przejrze rzeczywiste wartoci przechowywane w pamici. Nowoczesne
debuggery mog wywietla wartoci w postaci odpowiadajcej typowi rzeczywistej zmiennej, tj.
acuchy s mog by wywietlane jako napisy zoone ze znakw, zmienne typu long jako liczby,
a nie cztery bajty, i tak dalej. Wymylne debuggery C++ mog nawet pokazywa pene klasy i
wywietla biece wartoci wszystkich ich zmiennych skadowych, cznie ze wskanikiem
this.
Asembler
Cho czytanie kodu rdowego moe by wystarczajce do znalezienia bdu, jednak gdy
zawiod inne sposoby, istnieje moliwo poinstruowania debuggera, by wywietli kod