-
C++ Builder podporuje zpracovßnφ v²jimek C++, zpracovßnφ strukturovan²ch
v²jimek C a zpracovßnφ v²jimek VCL. P°φklady uvedenΘ v tΘto kapitole pro
C++ a C v²jimky mohou b²t p°elo₧eny jako konzolovΘ aplikace.
V²jimky popisujφ v²jimeΦnΘ situace, kterΘ vy₧adujφ specißlnφ zpracovßnφ
a mohou popisovat chyby vznikajφcφ za b∞hu aplikace (nap°. d∞lenφ nulou
nebo nedostatek volnΘho mφsta v pam∞ti). Zpracovßnφ v²jimek nabφzφ zp∙sob
°eÜenφ chyb. Obsluhu v²jimky m∙₧eme pou₧φt pro specifikaci akce, kterß
bude provedena po vzniku chyby p°ed ukonΦenφm programu. Jsou zpracovßvßny
pouze synchronnφ v²jimky, tzn. v²jimky, kterΘ jsou generovßny naÜφ aplikacφ.
Jazyk C++ urΦuje, ₧e vÜechny v²jimky vznikajφ v bloku try (chrßn∞n²
blok). Tento blok je nßsledovßn jednφm nebo vφce bloky catch, kterΘ
identifikujφ a zpracujφ v²jimky generovanΘ v bloku try.
Zpracovßnφ v²jimek vy₧aduje pou₧itφ t°φ klφΦov²ch slov: try,
catch
a throw. Kdy₧ program C++ zp∙sobφ v²jimku, pak °φzenφ je p°edßno
na jinou Φßst programu (obsluhu v²jimky), kterß zpracovßvß tento typ v²jimky.
Obsluha v²jimku zachycuje.
Program v²jimku generuje provedenφm p°φkazu throw. P°φkaz throw
se obecn∞ vyskytuje uvnit° funkce a mß tvar:
throw "overflow";
Tento p°φkaz vytvo°φ objekt, kter² popisuje typ v²jimky (v naÜem p°φpad∞
aritmetickΘ p°eteΦenφ). Jinß Φßst programu m∙₧e zachytit vygenerovan² objekt
v²jimky a zpracovat jej. K pou₧itφ zpracovßnφ v²jimek, musφme obklopit
nßÜ k≤d konstrukcφ try / catch. Syntaxe tΘto konstrukce je:
blok-try:
try slo₧en²-p°φkaz seznam-obsluh
seznam-obsluh:
obsluha [seznam-obsluh]
obsluha:
catch (deklarace-v²jimky)
slo₧en²-p°φkaz
deklarace-v²jimky:
seznam-specifikacφ-typ∙ deklarßtor
seznam-specifikacφ-typ∙ abstraktnφ-deklarßtor
seznam-specifikacφ-typ∙
p°φkaz-throw:
throw [p°i°azovanφ-v²raz]
KlφΦovß slova try, catch a throw nejsou v jazyku
C povolena. Blok try musφ b²t nßsledovßn blokem catch. P°φkazy
v bloku try jsou provßd∞ny b∞₧n²m zp∙sobem. Pokud v bloku try
je generovßna v²jimka, pak °φzenφ provßd∞nφ programu je p°edßno na p°φsluÜnou
obsluhu v²jimky. Obsluha je blok k≤du urΦen² k zpracovßnφ v²jimky. V jazyku
C++ musφ b²t za blokem try alespo≥ jedna obsluha. Program musφ obsahovat
obsluhu pro ka₧dou v²jimku, kterß m∙₧e b²t programem generovßna.
P°esto₧e v²jimky mohou b²t libovolnΘho typu, je vhodnΘ vytvß°et objekty
v²jimek. Objekty v²jimek jsou brßny jako libovolnΘ jinΘ objekty. V²jimka
nese informace z mφsta vzniku v²jimky do mφsta jejφho zachycenφ. Jednß
se o informaci, kterou u₧ivatel aplikace pot°ebuje znßt p°i v²skytu chyby
za b∞hu aplikace. C++ mß °adu p°eddefinovan²ch v²jimek.
Blok k≤du, ve kterΘm v²jimka m∙₧e vzniknout, musφ p°edchßzet klφΦovΘ
slovo try. Jestli₧e v²jimka nastane, pak b∞h programu je p°eruÜen
a program hledß p°φsluÜnou obsluhu v²jimky. Pokud obsluha je nalezena,
pak °φzenφ je na nφ p°edßno, nenφ-li nalezena, pak volßnφm set_terminate
mohla b²t nastavena funkce, kterß bude provedena; jinak program volß funkci
terminate.
Pokud ₧ßdnß v²jimka nenastane, pak program je proveden normßln∞.
Nßsleduje °ada p°φklad∙, ve kter²ch jsou pou₧ity r∙znΘ zp∙soby generovßnφ
v²jimek. Nßsledujφcφ p°φklad p°edßvß objekt Out obsluze (je pot°eba
jej p°elo₧it jako konzolovou aplikaci):
#include <conio.h>
#include <iostream.h>
bool pass;
class Out{};
void festival(bool
firsttime){ if(firsttime) throw Out();
}
int main()
{
try
{ pass = true;
festival(true);
}
catch(Out&
e){ pass = false; }
cout <<
(pass ? "true" : "false") << endl;
getch();
return 0;
}
Zde v bloku try je volßna funkce festival, kterß generuje
objekt Out. Tento objekt je zachycen obsluhou (je zde zm∞n∞na hodnota
prom∞nnΘ pass na false). Nßsledujφcφ p°φklad generuje zachycenou
v²jimku znova (p°φkaz throw bez parametru). V²jimka ji₧ musφ existovat.
#include <conio.h>
#include <iostream.h>
bool pass;
class Out{};
void festival(bool
firsttime){ if(firsttime) throw Out(); }
void test()
{
try { festival(true);
}
catch(Out&
e){ pass = false; throw;
}
}
int main()
{
try
{ test();
}
catch(Out&
e){ pass = true; }
cout <<
(pass ? "true" : "false") << endl;
getch();
return 0;
}
Funkce test volß funkci festival, ve kterΘ je generovßna
v²jimka
Out. V²jimka je zachycena obsluhou v test a op∞tovn∞
generovßna (p°edßna ven z funkce). DalÜφ p°φklad volß nßmi nastavenou ukonΦujφcφ
funkci (my_terminate) p°i pokusu o op∞tovnΘ generovßnφ v²jimky,
kterß nenastala.
#include <conio.h>
#include <iostream.h>
bool pass;
class Out{};
void my_terminate(){
cout <<
"Äßdnß v²jimka" << endl;
getch();
exit(1);
}
void festival(bool
firsttime){ if(firsttime) throw Out(); }
void test()
{
try { festival(false);
}
catch(Out&
e){ pass = false; }
throw;
// nelze op∞tovn∞ generovat v²jimku, kterß nenastala
}
int main()
{
set_terminate(my_terminate);
try {
test();
}
catch(Out&
e){ pass = true; }
cout <<
(pass ? "true" : "false") << endl;
getch();
return 0;
}
-
DalÜφ p°φklad specifikuje seznam v²jimek, kterΘ mohou b²t generovßny jednotliv²mi
funkcemi (festival a test). Ve funkci festival m∙₧e
vznikat v²jimka Out a ve funkci test ₧ßdnß v²jimka.
#include <conio.h>
#include <iostream.h>
bool pass;
class Out{};
void festival(bool
firsttime) throw(Out) // pouze v²jimka Out
{
if(firsttime)
throw Out();
}
void test() throw()
// ₧ßdnß v²jimka
{
try {
festival(true);
}
catch(Out&
e){ pass = true; }
}
int main()
{
pass = false;
test();
cout<<(pass?"test
zpracoval v²jimku":"v²jimka nezpracovßna")<<endl;
getch();
return 0;
}
Pokud festival generuje jinou v²jimku ne₧ Out, pak je
pova₧ovßna za neoΦekßvanou v²jimku a °φzenφ programu je p°edßno na funkci
unexpected,
jak je ukßzßno v nßsledujφcφm p°φklad∞.
Nßsledujφcφ p°φklad ukazuje test, kterß nemß generovat ₧ßdnou
v²jimku. Pokud n∞jakß funkce (nap°. operßtor new) v t∞le test
generuje v²jimku, pak v²jimka musφ b²t zpracovßna uvnit° test. Jinak
v²jimka poruÜuje seznam specifikacφ v²jimek pro test. Funkcφ set_unexpected
m∙₧eme nastavit jinou funkci pro neoΦekßvanou v²jimku; jinak, je volßna
funkce unexpected.
#include <conio.h>
#include <iostream.h>
bool pass;
class Out{};
void my_unexpected(){
cout <<
"Chyba test" << endl;
getch();
exit(1);
}
void festival(bool
firsttime) throw(Out) // pouze v²jimka Out
{
if(firsttime)
throw Out();
}
void test() throw()
// ₧ßdnß v²jimka
{
try { festival(true);
}
catch(Out&
e){ pass = true; throw; } // op∞tovnΘ generovßnφ
Out - chyba
}
int main()
{
set_unexpected(my_unexpected);
pass = false;
test();
cout<<(pass?"test
zpracoval v²jimku":"v²jimka nezpracovßna")<<endl;
getch();
return 0;
}
Kdy₧ v²jimka nastane, pak p°φkaz-throw inicializuje doΦasn²
objekt typu T (typ parametru par) pou₧it² v throw(T par).
DalÜφ kopie mohou b²t generovßny podle po₧adavk∙ p°ekladaΦe. Je tedy u₧iteΦnΘ
definovat pro objekt v²jimky kopφrovacφ konstruktor, jak je ukßzßno v nßsledujφcφm
p°φklad∞.
#include <conio.h>
#include <iostream.h>
class festival
{
public:
festival()
{ cout << "vytvo°enφ festival" << endl; }
festival(const
festival&){cout<<"vytvo°enφ kopie festival" << endl;}
~festival()
{ cout << "zruÜenφ festival" << endl; }
};
int main()
{
try {
cout << "generovßnφ v²jimky festival" << endl;
throw(festival());
}
catch(festival&){
cout << "vytvo°enφ festival" << endl;
}
getch();
return 0;
}
-
Obsluha v²jimky je specifikovßna klφΦov²m slovem catch, kterΘ je
umφst∞no bezprost°edn∞ za blok try. M∙₧e se takΘ vyskytnout bezprost°edn∞
za jin²m blokem catch. Ka₧dß v²jimka generovanß programem musφ b²t
zachycena a zpracovßna obsluhou v²jimky. Obsluha zachycuje v²jimku, kdy₧
se jejφ typ shoduje (nebo m∙₧e b²t p°evedena) s typem v p°φkazu catch.
P°i shod∞ je °φzenφ p°edßno obsluze. Obsluha urΦuje, kterΘ akce majφ b²t
provedeny. Po dokonΦenφ obsluhy, program pokraΦuje za poslednφ obsluhou
pro souΦasn² blok try. Äßdnß dalÜφ obsluha pro souΦasnou v²jimku
nenφ vyhodnocena. K p°enosu °φzenφ mimo obsluhu m∙₧e b²t pou₧it p°φkaz
goto.
Pokud vznikne chyba p°i provßd∞nφ obsluhy v²jimky, pak program je ukonΦen.
Obsluhu v²jimek si ukß₧eme na nßsledujφcφm p°φklad∞.
#include <conio.h>
#include <iostream.h>
class festival{};
class Harvest : public
festival{};
class Spring : public
festival{};
void ToHaveFun(int
i)
{
if(i==1) throw(Harvest());
else
throw(Spring());
}
int main()
{
try {
ToHaveFun(0);
}
catch(const
Harvest&){cout << "generovßno Harvest Festival" << endl;
}
catch(const
Spring&) {cout << "generovßno Spring Festival" << endl;
}
try {
ToHaveFun(1);
}
catch(const
Harvest&){cout << "generovßno Harvest Festival" << endl;
}
catch(const
Spring&) {cout << "generovßno Spring Festival" << endl;
}
getch();
return 0;
}
DalÜφ p°φklad ukazuje, ₧e kdy₧ zachytφme v²jimku a tato v²jimka je
souΦßstφ hierarchie t°φd, pak je nutno zaΦφt zachytßvßnφm nejvφce odvozenΘ
t°φdy v²jimky.
#include <conio.h>
#include <iostream.h>
class festival{};
class harvest
: public festival{};
class spring : public
festival{};
void ToHaveFun(int
i)
{
if (i==1)
throw(harvest());
else if(i==2)
throw(spring());
else throw(festival());
}
int main()
{
/* P°i zachycovßnφ
v²jimek zßle₧φ na po°adφ */
try { ToHaveFun(0);
}
catch(const
harvest&){cout << "generovßno Harvest Festival" << endl;
}
catch(const
spring&) {cout << "generovßno Spring Festival" << endl;
}
catch(const
festival& ){cout << "generovßno Festival" << endl; }
try { ToHaveFun(1);
}
catch(const
harvest&){cout << "generovßno Harvest Festival" << endl;
}
catch(const
spring&) {cout << "generovßno Spring Festival" << endl;
}
catch(const
festival& ){cout << "generovßno Festival" << endl; }
try { ToHaveFun(2);
}
catch(const
harvest&){cout << "generovßno Harvest Festival" << endl;
}
catch(const
spring&) {cout << "generovßno Spring Festival" << endl;
}
catch(const
festival& ){cout << "generovßno Festival" << endl; }
/* Zachytßvßnφ
zßkladnφ t°φdy d°φve ne₧ odvozen²ch t°φd zp∙sobφ, ₧e
odvozenß v²jimka je zachycena obsluhou zßkladnφ t°φdy v²jimky */
try { ToHaveFun(1);
}
catch(const
festival&){cout<<"generovßno Festival (je sprßvnΘ?)"<<endl;}
catch(const
harvest&){cout << "generovßno Harvest Festival" << endl;
}
catch(const
spring&) {cout << "generovßno Spring Festival" << endl;
}
try { ToHaveFun(2);
}
catch(const
festival&){cout <<"generovßno Festival (je sprßvnΘ?)"<<endl;}
catch(const
harvest&){cout << "generovßno Harvest Festival" << endl;
}
catch(const
spring&) {cout << "generovßno Spring Festival" << endl;
}
getch();
return 0;
}
V nßsledujφcφm p°φkladu p°φkaz catch (...) zachytφ v²jimku libovolnΘho
typu. Tento p°φkaz je jedinß obsluha v²jimky bloku try.
#include <conio.h>
#include <iostream.h>
bool pass;
class Out{};
void festival(bool
firsttime) throw(Out)
{
if(firsttime)
throw Out();
}
void test() throw()
{
try { festival(true);
}
catch(...){
pass = true; }
}
int main()
{
pass = false;
test();
cout<<(pass?"test
zpracoval v²jimku":"v²jimka nezpracovßna")<<endl;
getch();
return 0;
}
-
C++ poskytuje slu₧bu nazvanou specifikace v²jimek, kterß umo₧nφ specifikovat
seznam v²jimek, kterΘ funkce m∙₧e generovat. Tato specifikace se zapisuje
jako p°φpona deklarace funkce. Pou₧itφ p°φpony specifikacφ v²jimek neovliv≥uje
typ funkce. V p°edchozφch p°φkladech specifikace v²jimek ji₧ byla pou₧ita.
Nßsledujφ ukßzky deklaracφ funkcφ se specifikacemi v²jimek:
void f1();
// Funkce generuje libovolnΘ v²jimky
void f2() throw();
// Funkce negeneruje ₧ßdnou v²jimku
void f3() throw(
A, B* ); // Funkce m∙₧e generovat v²jimky odvozenΘ od A
// nebo od ukazatele na B
Pokud funkce generuje v²jimku neuvedenou ve specifikaci, pak program
volß unexpected.
-
Win32 podporuje zpracovßnφ strukturovan²ch v²jimek C, kterΘ se podobajφ
v²jimkßm C++. Jsou zde jistΘ odchylky, co₧ vy₧aduje opatrnost p°i mφchßnφ
t∞chto dvou typ∙ v²jimek. P°i pou₧φvßnφ strukturovan²ch v²jimek je nutno
v aplikacφch C++ Builderu dodr₧ovat tato pravidla:
-
StrukturovanΘ v²jimky mohou b²t pou₧ity v C++ programech.
-
C++ v²jimky nemohou b²t pou₧φvßny v C programech (klφΦovΘ slovo catch
nenφ p°φpustnΘ v C).
-
V²jimky generovanΘ volßnφm funkce RaiseException jsou zpracovßny
blokem try / __except (C++) nebo __try / __except (C). Je
mo₧no pou₧φt i bloky try /__finally nebo __try / __finally.
P°i volßnφ RaiseException jsou ignorovßna zpracovßnφ bloky try
/ catch.
-
V²jimky, kterΘ nejsou zpracovßny obsluhou, nevolajφ terminate, ale
jsou p°edßny operaΦnφmu systΘmu (koneΦn²m v²sledkem je ukonΦenφ procesu).
-
Zpracovßnφ v²jimek nevy₧aduje kopii objektu v²jimky, pokud ji nevy₧adujeme
sami.
V C programech pou₧φvßme k implementaci strukturovan²ch v²jimek klφΦovß
slova __except, __finally a __try. KlφΦovΘ slovo __try
lze pou₧φt pouze v C programech. Pokud chceme zapsat p°enositeln² k≤d,
pak strukturovanΘ v²jimky nenφ vhodnΘ pou₧φvat.
StrukturovanΘ v²jimky mohou b²t zpracovßny pomocφ rozÜφ°enφ zpracovßnφ
C++ v²jimek:
try {
funkce();
}
__except(__expr__)
{
// obsluha
}
__expr__ je v²raz, jeho₧ vyhodnocenφ je jedna ze t°ech hodnot:
EXCEPTION_CONTINUE_SEARCH
(0 - obsluha nenφ zahßjena a OS pokraΦuje v hledßnφ obsluhy v²jimky), EXCEPTION_CONTINUE_EXECUTION
(-1 - provßd∞nφ pokraΦuje v bod∞ v²jimky) a EXCEPTION_EXECUTE_HANDLER(1
- obsluha v²jimky je provedena).
Win32 poskytuje dv∞ funkce, kterΘ mohou b²t pou₧ity k zjiÜt∞nφ informacφ
o aktivnφ v²jimce: GetExceptionCode a GetExceptionInformation.
Jestli₧e chceme tyto funkce volat jako Φßst v²razu filtru, pak je musφme
volat p°φmo v __except. V²raz filtru slou₧φ k filtrovßnφ v²jimek.
-
Pokud mφchßme C++ a strukturovanΘ v²jimky, pak nesmφme zapomenout na n∞kterΘ
v∞ci. I kdy₧ C++ Builder implementuje C++ v²jimky se strukturovan²mi v²jimkami
Win32, pak v bloku __except jsou v²jimky C++ transparentnφ. Blok
try
m∙₧e b²t nßsledovßn jednφm blokem __except nebo alespo≥ jednφm blokem
catch.
Tyto bloky nelze mφchat, ale je mo₧no vno°it dva bloky try do sebe:
try {
EXCEPTION_POINTERS
*xp;
try {
funkce();
}
__except(xfilter(xp
= GetExceptionInformation())) {
//...
}
}
catch (...) {
//...
}
Mφchßnφ v²jimek si ukß₧eme na nßsledujφcφm p°φklad∞. P°elo₧te jej jako
konzolovou aplikaci a pokuste se pochopit, kdy v²jimky vznikajφ a jak jsou
zpracovßny.
#include <iostream.h>
#include <conio.h>
class Exception
{
public:
Exception(char*
s = "Unknown"){ what = strdup(s); }
Exception(const
Exception& e ){ what = strdup(e.what); }
~Exception()
{ delete[] what; }
char* msg()
const
{ return what;
}
private:
char* what;
};
int main()
{
float e, f,
g;
try {
try {
f = 1.0;
g = 0.0;
try {
cout << "v²jimka: " << endl;
e = f / g;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
cout << "Zachycenφ strukturovanΘ v²jimky. " << endl;
throw(Exception("Chyba: D∞lenφ 0"));
}
}
catch(const Exception& e) {
cout << "Zachycenφ C++ v²jimky: " << e.msg() << endl;
}
}
__finally
{
cout << "C++ umo₧≥uje takΘ pou₧φt __finaly " << endl;
}
getch();
return e;
}
-
Jak ji₧ jsme vid∞li v p°edchozφm p°φklad∞, model zpracovßnφ strukturovan²ch
v²jimek podporuje ukonΦujφcφ blok (__finally). Tento blok je provßd∞n
v₧dy (kdy₧ v²jimka vznikne i kdy₧ nevznikne).
try {
funkce();
}
__finally {
// zde uvedenΘ
p°φkazy jsou provedeny v₧dy
}
Pou₧itφ ukonΦovacφho bloku je uvedeno v nßsledujφcφm p°φklad∞.
#include <conio.h>
#include <iostream.h>
int main()
{
float e, f,
g;
try {
f = 1.0;
g = 0.0;
try {
cout << "V²jimka: " << endl;
e = f / g;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
cout << "Zachycenφ v²jimky. " << endl;
}
}
__finally
{
cout << "Je provedeno takΘ. " << endl;
}
try {
f = 1.0;
g = 2.0;
try {
cout << "Nenφ v²jimka: " << endl;
e = f / g;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
cout << "Zachycenφ v²jimky. " << endl;
}
}
__finally
{
cout << "Je provedeno takΘ. " << endl;
}
getch();
return e;
}