You are on page 1of 26

Rozdzia 20.

Wyjtki i obsuga bdw


Kod zawartych w tej ksice przykadw zosta stworzony w celach ilustracji. Aby nie odwraca
twojej uwagi od prezentowanych tutaj zagadnie, nie zastosowano w nich adnych mechanizmw
obsugi bdw. Jednak w prawdziwych programach obsuga bdw jest wana.
Z tego rozdziau dowiesz si:

czym s wyjtki,

jak uywa wyjtkw i jakie s rezultaty ich dziaania,

jak budowa hierarchie wyjtkw,

Jak traktowa wyjtki w stosunku do obsugi bdw,

czym jest debugger.

Pluskwy, bdy, pomyki i psujcy si kod


Wszystkie programy zawieraj pluskwy (czyli bdy). Im wikszy program, tym wicej pluskiew i
wiele z nich przedostaje si do ostatecznej jego wersji. Tworzenie stabilnych, wolnych od
pluskiew programw powinno by priorytetem kadego, kto powanie myli o programowaniu.
Najwaniejszym problemem przy tworzeniu oprogramowania jest bdny, niestabilny kod. W
wielu powanych przedsiwziciach informatycznych najwikszym wydatkiem jest jego
testowanie i poprawianie. Kto, kto wpadnie na pomys, jak tworzy niskim kosztem i na czas
dobre, solidne i odporne programy, zrewolucjonizuje przemys oprogramowania.
Kopoty z programem mog by powodowane przez kilka rnych rodzajw bdw. Pierwszym z
nich jest bdna logika: program robi to, co ma robi, ale algorytm nie zosta waciwie
przemylany. Drugim rodzajem bdw jest syntaktyka: niewaciwa konstrukcja, funkcja, czy
struktura. Te dwa rodzaje bdw wystpuj najczciej i szuka ich wikszo programistw.

Badania i dowiadczenie programistw wykazay, e im pniej zostanie wykryty bd, tym


bardziej kosztowne staje si jego usunicie. Najmniej kosztowne bdy i pomyki to te, ktrych
uda si unikn. Kolejne mao kosztowne bdy to bdy wykrywane przez kompilator. Standardy
jzyka C++ wymuszaj na kompilatorze wychwytywanie coraz wikszej iloci bdw ju podczas
kompilacji.
Bdy, ktre zostay wkompilowane i zostay wychwycone przy pierwszym tecie tj. te, ktre
niezmiennie powoduj zaamanie programu s mniej kosztowne w wyszukaniu i poprawieniu
ni bdy, ktre powoduj zaamanie programu dopiero po pewnym czasie.
Czstszym problemem ni bdy logiczne lub syntaktyczne jest wraliwo programu: dziaa on
poprawnie, gdy uytkownik wpisuje liczb tam, gdzie powinien j wpisa, lecz zaamuje si, gdy
uytkownik wpisze litery (zamiast liczby). Inne programy zaamuj si po wyczerpaniu si
pamici, gdy dyskietka zostanie wyjta ze stacji lub gdy modem zerwie poczenie.
Aby walczy z tego rodzaju bdami, programici staraj si uodporni swoje programy.
Odporny program potrafi obsuy wszystko, co moe si wydarzy podczas jego dziaania, od
dziwnych danych wprowadzanych przez uytkownika po nagy brak pamici.
Naley dokona rozrnienia pomidzy pluskwami, ktre powstay, poniewa pomyli si
programista; bdami logicznymi, ktre powstay, poniewa programista nie zrozumia
zagadnienia lub nie wie, jak sobie z nim poradzi, oraz wyjtkami, ktre powstaj, gdy wystpia
niezwyky, cho przewidywalny problem, taki jak wyczerpanie si zasobw (pamici czy miejsca
na dysku).

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,

poinformowanie uytkownika i zamknicie programu,

poinformowanie uytkownika i pozwolenie mu na zwolnienie dodatkowej pamici i podjcie


ponownej prby,

podjcie odpowiednich dziaa i kontynuowanie pracy bez niepokojenia uytkownika.

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,

wyjtki stanowi ekspresow ciek od kodu alokujcego zasoby do kodu mogcego


obsuy sytuacj wyjtkow. Gdy pomidzy nimi wystpuj inne warstwy funkcji
interwencyjnych, majdaje im si moliwo uporzdkowania zaalokowanej pamici. Nie
wymaga si od nich jednak, aby musz jednak zawieray wycznie kodu, ktrego jedynym
przeznaczeniem jest przekazywaniewyej informacji o bdzie dalej.

Jak uywane s wyjtki


Obszary kodu, ktre mog powodowa problem, ujmowane s w bloki try (sprbuj). Na
przykad:
try
{
SomeDangerousFunction(); // potencjalnie niebezpieczna funkcja
}

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
}

Podstawowe etapy obsugiwania wyjtkw to:


1. Zidentyfikowanie tych obszarw programu, w ktrych zaczynaj si operacje mogce
powodowa wyjtek i umieszczenie ich w blokach try.
2. Stworzenie blokw catch wychwytujcych zgaszane wyjtki, porzdkujce zaalokowan
pami i ewentualnie informujce uytkownika. Listing 20.1 przedstawia uycie zarwno blokw
try, jak i catch.
Wyjtki s obiektami uywanymi do przekazywania informacji o problemie.
Blok try jest blokiem ujtym w nawiasy klamrowe, wewntrz ktrego mog by zgaszane
wyjtki.
Blok catch jest blokiem wystpujcym bezporednio po bloku try; s w nim obsugiwane
zgoszone wyjtki.
Gdy zostanie zgoszony wyjtek, sterowanie przechodzi do waciwego bloku catch
nastpujcego po biecym bloku try.

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.

Listing 20.1. Zgaszanie wyjtku


0:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:

#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 {};

// definiuje klas wyjtku

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
}

Uycie blokw try oraz blokw catch


Okrelenie, w ktrym miejscu naley umieci bloki try, nie jest atwe: nie zawsze oczywiste
jest, ktre dziaania mog powodowa wyjtki. Nastpn zagadk jest to, gdzie wyjtek ma zosta
wychwycony. By moe zechcemy zgasza wszystkie wyjtki pamici podczas operacji
alokowania pamici, ale moe te bdziemy chcie wyapywa je na wyszym poziomie programu
(tym wsppracujcym z interfejsem uytkownika).
Prbujc wyznaczy lokalizacje dla blokw try, poszukaj tych miejsc, w ktrych alokujesz
pami lub uywasz zasobw. Inne bdy, ktre moesz wyapywa, to bdy zakresu,
niewaciwych danych wejciowych, itd.

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.

Wychwytywanie wicej ni jednego rodzaju


wyjtkw
Istnieje moliwo, e wyjtek moe zosta spowodowany przez wicej ni jedn sytuacj
wyjtkow. W takim przypadku instrukcje catch mog by ukadane jedna za drug, podobnie
jak bloki case w instrukcji switch. Odpowiednikiem bloku default jest instrukcja wychwy
wszystko, zapisywana jako catch(...). Listing 20.2 przedstawia wychwytywanie wicej ni
jednego rodzaju wyjtkw.
Listing 20.2. Wyjtki wielokrotne
0:
1:
2:
3:
4:
5:

#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:

pType = new int[size];


for (int i = 0; i<size; i++)
pType[i] = 0;
}
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)
{
cout << "Poprosiles o tablice";
cout << " zawierajaca zero obiektow!\n";
}
catch (...)
{
cout << "Cos poszlo nie tak!\n";
}
cout << "Gotowe.\n";
return 0;
}

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();

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]; // 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

}
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:

cout << "Poprosiles o tablice";


cout << " zawierajaca zero obiektow!\n";
}
catch (...)
{
cout << "Cos poszlo nie tak!\n";
}
cout << "Gotowe.\n";
return 0;
}

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.

Dane w wyjtkach oraz nazwane obiekty


wyjtkw
Aby mc odpowiednio zareagowa na bd, czsto chcemy wiedzie wicej; nie tylko zna typ
zgoszonego wyjtku. Klasy wyjtkw s takie same jak inne klasy, wic mona w nich
umieszcza dane, inicjalizowa je w konstruktorze i w dowolnym momencie odczytywa. Ilustruje
to listing 20.4.
Listing 20.4. Odczytywanie danych z obiektu wyjtku
0:

#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:

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(){}
int GetSize() { return itsSize; }
private:
int itsSize;
};
class xTooBig : public xSize
{
public:
xTooBig(int size):xSize(size){}
};
class xTooSmall : public xSize
{
public:
xTooSmall(int size):xSize(size){}
};
class xZero : public xTooSmall
{
public:
xZero(int size):xTooSmall(size){}
};
class xNegative : public xSize
{
public:
xNegative(int size):xSize(size){}
};
private:
int *pType;

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:

catch (Array::xTooBig theException)


{
cout << "Ta tablica jest zbyt duza...\n";
cout << "Otrzymano " << theException.GetSize() << endl;
}
catch (Array::xTooSmall theException)
{
cout << "Ta tablica jest zbyt mala...\n";
cout << "Otrzymano " << theException.GetSize() << endl;
}
catch (...)
{
cout << "Cos poszlo nie tak, ale nie mam pojecia co!\n";
}
cout << "Gotowe.\n";
return 0;
}

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.

Indywidualne wypisywanie odpowiednich komunikatw przez poszczeglne instrukcje catch jest


kopotliwe i podatne na bdy. To zadanie naley do obiektu, ktry zna swj typ i zawarte w nim
wartoci. Listing 20.5 przedstawia bardziej obiektowo zorientowane podejcie do tego problemu,
w ktrym kady wyjtek sam wykonuje odpowiedni prac.
Listing 20.5. Przekazywanie przez referencj i uycie funkcji wirtualnych w wyjtkach

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:

cout << "Zly rozmiar!\n";


}
cout << "Gotowe.\n";
return 0;
}

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.

Wyjtki bez bdw


Gdy programici C++ spotykaj si po pracy w cyberprzestrzennym barze przy wirtualnym piwie,
czsto rozmawiaj o tym, czy w rutynowych sytuacjach powinny by uywane wyjtki. Niektrzy
utrzymuj, e z racji swojej natury, wyjtki powinny by zarezerwowane dla tych wyjtkowych
(std nazwa!), lecz przewidywalnych sytuacji, ktre programista musi wzi pod uwag i nie
powinny by czci rutynowego dziaania kodu.
Inni wskazuj, e wyjtki oferuj wygodny i przejrzysty sposb powrotu poprzez wiele poziomw
wywoa funkcji bez ryzyka powstawania wyciekw pamici. Czsto przytaczany jest nastpujcy
przykad: uytkownik da dziaania w rodowisku GUI. Przechwytujca danie cz kodu musi
wywoa funkcj skadow w menederze dialogw, ktry z kolei wywouje kod przetwarzajcy
danie, ktry wywouje kod decydujcy o tym, ktre okno dialogowe ma by uyte; ten kod z
kolei wywouje kod tworzcy okno dialogowe, ktre nastpnie wywouje kod obsugujcy
dziaania uytkownika w oknie. Gdy uytkownik kliknie przycisk Anuluj, kod musi powrci do
pierwszej metody wywoujcej, w ktrej byo obsugiwane pierwotne danie.
Jednym z rozwiza tego problemu jest umieszczenie bloku try wok wywoania pierwotnego i
wychwycenie CancelDialog (anuluj okno dialogowe) jako wyjtku, ktry moe zosta
zgoszony przez procedur obsugi przycisku Anuluj. To rozwizanie jest bezpieczne i efektywne.
Klikanie przycisku Anuluj nie jest bdem, lecz standardow operacj w interfejsie uytkownika.

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.

Kilka sw na temat psujcego si kodu


Psucie si kodu jest powszechnie znanym zjawiskiem, w ktrym, z powodu zaniedbania
pogarsza si jako oprogramowania. Doskonale napisany, w peni przetestowany program
zmienia si w bdny ju po kilku tygodniach od dostarczenia go do klienta. Po kilku miesicach
klient zauway, e programowi brakuje logiki i e wiele obiektw zaczyna si rozsypywa.
Oprcz dostarczania kodu rdowego w opakowaniach prniowych, twoj jedyn ochron jest
pisanie programw w taki sposb, by mc szybko i atwo zidentyfikowa problem, gdy wrcisz do
nich w celu poprawienia jakiego elementu.

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.

To oznacza, e kod musi by napisany zrozumiale i uzupeniony komentarzami tam, gdzie


korzystasz z jakich sztuczek. Sze miesicy po dostarczeniu kodu bdziesz go czyta jak kto
zupenie obcy, zastanawiajc si ze zgroz, jak kto mg stworzy tak udziwnion skadni.

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.

Penoekranowe symboliczne debuggery bardzo uatwiaj odpluskwianie kodu. Gdy adujesz


debugger, odczytuje on cay kod rdowy i wywietla go w oknie. Moesz przechodzi przez
wywoania funkcji lub nakaza wejcie do poszczeglnych funkcji, wykonujc kod linia po linii.
W wikszoci debuggerw mona przecza si pomidzy kodem rdowym a wynikami
programu tak, aby mc zobaczy wynik wykonania poszczeglnych instrukcji. Mona sprawdza
biecy stan kadej zmiennej, przeglda zoone struktury danych oraz przeglda pami
wskazywan przez wskaniki lub zajmowan przez obiekty. W debuggerze mona wykonywa
take rne operacje, takie jak ustawianie punktw wstrzymania, ledzenie wartoci zmiennych,
sprawdzanie pamici i przegldanie kodu maszynowego.

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.

ledzenie wartoci zmiennych


Istnieje moliwo poinstruowania debuggera, by wywietli warto okrelonej zmiennej lub by
wstrzyma dziaanie koduy w momencie, gdy nastpuje zapis lub odczyt zmiennej. Punkty
ledzenia (ang. watch points) pozwalaj na okrelenie tych warunkw, a czasem nawet na
modyfikowanie wartoci zmiennej podczas dziaania programu.

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

maszynowy wygenerowany dla poszczeglnych linii programu. Mona sprawdza wartoci


rejestrw i znacznikw procesora i analizowa dziaanie programu dosownie do goego metalu.
Naucz si korzysta ze swojego debuggera. Moe by on najlepsz broni w witej wojnie
przeciwko pluskwom. Bdy czasu dziaania s najtrudniejsze do znalezienia i usunicia, a
wydajny debugger moe umoliwi (nie tylko uatwi) wyszukanie prawie wszystkich z nich.

You might also like