450 likes | 562 Views
KIVÉTELKEZELÉS. Programozási nyelvek összehasonlító elemzése. Készítette: Pásztor Attila KF GAMF ELTE-IK DOKTORI ISKOLA PHD hallgató. KIVÉTELKEZELÉS. Tartalom:. Kivételkezelés, kivétel fogalmai Kezeletlen kivétel ”Kivételkezelés” a C nyelvben
E N D
KIVÉTELKEZELÉS Programozási nyelvek összehasonlító elemzése Készítette: Pásztor Attila KF GAMFELTE-IK DOKTORI ISKOLAPHD hallgató
KIVÉTELKEZELÉS Tartalom: • Kivételkezelés, kivétel fogalmai • Kezeletlen kivétel • ”Kivételkezelés” a C nyelvben • A kivételkezelés kezdetei : FORTRAN, COBOL, PL/I • Fejlett kivételkezelés :C++, JAVA, C# • Kivételkezelés adatbáziskezelő nyelvekben : TSQL, PLSQL, FOXPRO • Egyebek • Összefoglalás
Elvárás: az előllított program helyesen és megbízhatóan működjön Alapvető elvárás: helyes - a specifikációnak megfelelően működik robosztus – semmilyen körülmények között sem okozzon nem várt mellékhatást, katasztrófát
Kezeletlen kivétel class Program { static void Main(string[] args) { int a = Beolvas("z els"); int b = Beolvas(" második"); int c = a + b; Console.WriteLine("A két szám összege: {0}", c); } static int Beolvas(string Aktuális) { Console.Write("A"+Aktuális+" szám: "); string s = System.Console.ReadLine(); int a = int.Parse(s); return a; } }
Kivétel : olyan futás közben fellépő hiba ami megszakítja az utasítások normál futási sorrendjét A programok futása közben hibák léphetnek fel. • Szoftverhibák • a futtatott programban, • kísérlet zérus osztásra • null pointer értékre való hivatkozás • aritmetikai túlcsordulás, alulcsordulás • tömb hibás indexelése • hibás típus konverzió kísérlete • hibás input stb. • az operációs rendszerben meglévő programozói hibák. • Hardverhibák • elromlott winchester miatti leállások, • elfogy a memória, stb.
Kivételkezelés feladata : • olyan események kezelése melyek nehézzé, vagy lehetetlenné teszik a program normál módú folytatását • a futási hibák kijavítása (ha lehetséges), vagy a program leállítása úgy, hogy a legkisebb hibák keletkezzenek Kivételkezelés régen : - abortált a program :-( - minden alprogram valamilyen jelzést (hibaértéket) ad vissza, hogy a lefutás OK., vagy nem:
”Kivételkezelés” a C nyelvben intFGV() { FILE *ifptr=NULL; FILE *ofptr=NULL; char *bet=NULL; bet=calloc(1,1); ifptr=fopen("C:\\szöveg.txt","r"); ofptr=fopen("C:\\uj.txt","w"); do{ *bet=getc(ifptr); putc(*bet,ofptr); }while(!feof(ifptr)); fclose(ifptr); fclose(ofptr); free(bet); return 0; } Nem megbízható program ! Megoldások: 1-hibakezelés helyben a „veszélyes” helyeken 2- közös hibakezelő rész
”Kivételkezelés” a C nyelvben - hibakezelés helyben int FGV() { FILE *ifptr=NULL; FILE *ofptr=NULL; char *bet=NULL; bet=calloc(1,1); if(bet==NULL){printf(”kevés memória! "); getch(); return 1;} ifptr=fopen("C:\\szöveg.txt","r"); if(ifptr==NULL) {printf("HIBAS MEGNYITAS Olvasasra");free(bet);return 2;} ofptr=fopen("C:\\uj.txt","w"); if(ofptr==NULL) {printf("HIBAS MEGNYITAS Irasra"); fclose(ofptr);free(bet); getch();return 3;} do { *bet=getc(ifptr); //……….. További lehetőségek putc(*bet,ofptr); } while(!feof(ifptr)); fclose(ifptr); fclose(ofptr); free(bet); return 0; }
”Kivételkezelés” a C nyelvben- közös hibakezelő rész int FGV() { FILE *ifptr=NULL; FILE *ofptr=NULL; char *bet; bet=calloc(1,1); if(bet==NULL) goto hiba; ifptr=fopen("c:\\szoveg.txt","r"); if(ifptr==NULL)goto hiba; ofptr=fopen("c:\\uj.txt","w"); if(ofptr==NULL)goto hiba; do{ *bet=getc(ifptr); putc(*bet,ofptr); } while(!feof(ifptr)); hiba: if(ifptr!=NULL)fclose(ifptr); else{ printf("HIBA..");getch();} if(ofptr!=NULL)fclose(ofptr); else{ printf("HIBA..");getch();} if(bet!=NULL)free(bet); else{ printf("HIBA..");getch();} return 0; }
Kivételkezelés kezdetei - FORTRAN • Csak input – output műveletek közbeni hibák kezelése • err paraméter a hibakezelő rész címkéjével open(file=’proba.file’,err=100, unit=2) . . . 100 write(*,110) 110 format(’Hiba’)
Kivételkezelés kezdetei - COBOL • Aritmetikai műveletek hibáinak kezelése • Input –output műveletek közbeni hibák kezelése 1, DIVIDE N1 BY N2 GIVING N3 REMINDER N4 - eseti jellegű hibakezelés ON SIZE ERROR DISPLAY ” HIBA” - mint a FORTRAN-ban 2, PROCEDURE DIVISION. DECLERATIVES. Hiba SECTION. USE AFTER EXCEPTION PROCEDURE ON SajatFile. Hibakezeles2. DISPLAY ”HIBA” END DECLERATIVES
Kivételkezelés kezdetei – PL/I • Kezdetleges kivételkezelő mechanizmus (60-as évek) • Néhány beépített kivétel pl. ZERODIVIDE • A kivételeknek nevük van, nem lehet – paraméterezni – csoportosítani • Lehet saját kivételt készíteni – CONDITION • Lehet kivételt dobni - SIGNAL
Elvárások a kivételkezeléssel szemben meg tudjuk különböztetni a hibákat, a hibát kezelő kód különüljön el a tényleges kódtól, megfelelően tudjuk kezelni - a hiba könnyen jusson el arra a helyre, ahol azt kezelni kell, kötelező legyen kezelni a hibákat
Megoldás elemzése: Az első elvárás, hogy meg tudjuk különböztetni a hibákat - ez általában a fellépés helyén történik. A második elvárás azt szeretné elérni, hogy a programot először látó ember is azonnal el tudja különíteni a hibakezelő és a működésért felelős részeket. Ez könnyebben továbbfejleszthető, karbantartható programokhoz vezet.
A hiba jusson el oda, ahol kezelni kell A harmadik elvárás arra vonatkozik, hogy - mivel a programok többszintűek -, egy alacsonyabb szinten jelentkező hibát (például ha egy fájlt nem tudunk megnyitni, egy üres veremből akar valaki kiolvasni) nem biztos, hogy az adott szint le tud kezelni, ezért a megfelelő helyre kell eljuttatni.
Programnyelvek kivételkezeléseinek összehasonlítási szempontjai: Hiba- vagy kivételkezelést ad-e a nyelv? Hogyan kell kivételeket definiálni-kiváltani-kezelni? Milyen a kivételkezelés szintaktikai formája / Mihez kapcsolódik a kezelés: utasítás-, blokk- vagy eljárás/függvényszintű? A kivételekből lehet-e kivételcsoportokat, kivételosztályokat képezni, amelyeket a kivételkezelőben egységesen lehet kezelni? Paraméterezhető-e a kivétel információ továbbadás céljából? A kivétel kezelése után honnan folytatódjon a program?
Programnyelvek kivételkezeléseinek összehasonlítási szempontjai Van –e olyan nyelvi elem amely mind kivételes mind normál módú futás esetén végrehajtódik („finally”) ? Tovább dobódhat-e a kivétel? Mi történik a kezeletlen kivétellel? Megadható-e /meg kell-e adni, hogy egy alprogram milyen lekezeletlen kivételeket küldhet? Párhuzamos környezetben vannak-e speciális kivételek? Mi történik a váratlan kivételekkel? Kiváltható-e kivétel kivételkezelőben? Melyik kivételkezelő kezeli a kivételt?
Fejlett kivételkezelés :C++ • try { • // kivétel keletkezhet • } • catch( típus [név]){ • // kivétel kezelése • } • - Egy try blokkhoz több catch ág is tartozhat (mindegyik a saját kiv. tipust kapja el • - catch(…) esetén minden kivételt elkap • bármilyen típusú változót el lehet dobni kivételként • kivétel dobása throw • try { • // mindenféle kivétel keletkezhet • } • catch( ….){ • // minden kivételt elkap • }
Fejlett kivételkezelés :C++ • int main() { • FILE *fptr; • try { • fptr=fopen("c:\\kw.txt", "r"); • if( fptr ==NULL ) throw "baj van"; • } • catch(int) {cout << "Exception1" ;} • catch(float){cout << "Exception2" ;} • catch(...) {cout << "Exception3" ; } • fclose(fptr); • } • - nincs finally • kezeletlen kivétel továbbadódik a hívó függvény felé, ha a main() sem kezeli meghívódik a terminate függvény => alapesetben az abort függvényt hívja ez leállítja a prg. futását • a kivétel kezelése után a vezérlés nem kerül vissza abba a blokkba ahol a kivétel keletkezett – destruktor problémák (két dobott kivétel)
Fejlett kivételkezelés :C++ finally hiányának kezelése class File{ private:FILE *f; public: File(char *filenev) { f=fopen(filenev,"r"); } ~File(){fclose(f); cout<<"lezártam";} }; void Fgv() { File f("c:\\k.txt"); try{} // itt keletkezhet kiv. catch(...){} // kiv. kez // a destruktor miatt fájl lezárás mindenképpen } int main() {Fgv(); }
Lehetőség függvények fejlécében a dobható kivételek részhalmazát megadni throw "(" [type {"," type}] ")" Pl.: void f1(int x) throw(A,B,C); Az f1 függvény csak A,B és C típusú kivételt generál, vagy azok leszármazottait. int f2(int x) throw () -- f2 nem válthat ki kivételt! void f3 (int x) -- f3 bármit kiválthat! Ha specifikáltunk lehetséges kivételtípusokat, akkor minden más esetben a rendszer meghívja az unexpected() függvényt, melynek alapértelmezett viselkedése a terminate() függvény meghívása. Ez a set_unexpectedsegítségével szintén átállítható. Fejlett kivételkezelés :C++
Példák: class X {}; class Y: public X {}; class Z {}; void A() throw(X,Z) // A X,Y,Z-t dobhat { /* ... */ } void B() throw(Y,Z) { A(); // unexpected, ha A X-t dob, ok Y, Z típusúra } void C(); // semmit sem tudunk C-ről void D() throw() { C(); // ha C bármit kivált, unexpected -t hív throw 7; // unexpected-t hív } Fejlett kivételkezelés :C++
Fejlett kivételkezelés :C++ Előre definiált kivételek : bad_alloc; bad_cast; bad_typeid #include <iostream> using namespace std; int main() { try { double *t = new double [1000000000]; } catch(bad_alloc) { cout << "Elfogyott a memória\n"; } system("pause"); return 0; }
try { ... throw new EgyException (“parameter”); } catch (típus változónév) { ... } finally { ... } Fejlett kivételkezelés :Java
a C++ szintaxistól nem sokban különbözik. A kivételek mindig objektumok. finally nyelvi konstrukció, ezzel a Java megbízhatóbb programok írását segíti elő. Nincs catch(…) helyette catch(Exception e) Fejlett kivételkezelés :Java
Egy függvény által kiváltható kivételek specifikálása: void f(int x)throws EgyikException, MasikException; Minden kivétel a java.lang.Throwable leszármazottja. Ha olyan kivételt szeretnénk dobni, amely nem a Throwable gyermeke, akkor az fordítási hibát okoz. publicclass ExExample { void method1() { try { method2(3); } catch (Exception e) { e.printStackTrace(); } } void method2(int i) throwsException{ if (i == 0) trownewException("illegal argument: 0"); } } Fejlett kivételkezelés :Java
A kivételek két nagy csoportba sorolhatóak: ellenőrzöttek: Exception leszármazottjai Az ellenőrzött kivételek esetén fordítási hiba lép fel, ha nincsenek specifikálva vagy elkapva; illetve ha olyan ellenőrzött kivételt kívánunk elkapni, amely hatókörön kívül van. nem-ellenőrzöttek: Error leszármazottjai Futási időben keletkeznek nem kötelező elkapni őket Fejlett kivételkezelés :Java
Azért volt arra szükség, hogy a kivételeket a fenti két csoportba sorolják, mert számos olyan kivétel van, amely előre nem látható és fölösleges lenne mindenhol lekezelni őket. Ezeket a kivételeket nevezzük nem-ellenőrzött kivételeknek. Például nem ellenőrizzük le minden utasítás végrehajtása előtt, hogy van-e elég memóriánk stb. Fejlett kivételkezelés :Java
Fejlett kivételkezelés :Java Néhány predefinit nem ellenőrzött kivétel (a RuntimeException leszármazottjai): ArithmeticException, ClassCastException, IndexOutOfBoundsException,két alosztálya: ArrayIndexOutOfBoundsException,StringIndexOutOfBoundsException, NullPointerException. az Error leszármazottjai pl. OutOfMemoryError,StackOverflowError, stb.
Fejlett kivételkezelés :Java predefinit kivételek előfordulása: class A {// ... } class B extends A {// ... } class C { void X() { A a= new A; B b=(B)a; // ClassCastException } void Y() { int ia[]= new int[10]; for (int i=1; i<=10; i++)ia[i]=0; /* amikor i==10 lesz, ArrayIndexOutOfBoundsException */ } void Z() { C c=null; c.X(); // NullPointerException } }
Fejlett kivételkezelés :Java Kivételek kezelése try { // utasítások } catch (MyException e) { // utasítások MyException kezelésére } catch (AnotherException e) { // utasítások AnotherException kezelésére } catch (Exception e) { // utasítások AnotherException kezelésérefinally { // mindig végrehajtódóutasítások } Fontos a catch ágak sorrendje
class Program { static void Main(string[] args) { string s = "asdfg"; int a = 0; try { a = int.Parse(s); Console.WriteLine(a); } catch (FormatException exc) { Console.WriteLine(exc.Message); } Console.ReadKey(); } } Fejlett kivételkezelés :C#
Fejlett kivételkezelés :C# Példa 1: using System; class ExceptionTestClass { public static void Main() { int x = 0; try { int y = 100/x; } catch (ArithmeticException e) { Console.WriteLine("ArithmeticException Handler: {0}", e.ToString()); } catch (Exception e) { Console.WriteLine("Generic Exception Handler: {0}", e.ToString()); } } }
using System; class ArgumentOutOfRangeExample { static public void Main() { …. try { … } catch (ArgumentOutOfRangeException e) { Console.WriteLine("Error: {0}",e); } finally { Console.WriteLine(„It is always executed."); } } } Fejlett kivételkezelés :C#
Fejlett kivételkezelés :C# Példapogram a throw használatára; több catch ág class Program { static void Main(string[] args) { string s = "200"; int a = 0; try { a = int.Parse(s); if (a > 100) throw new Exception("nem lehet nagyobb, mint 100!\n"); Console.WriteLine(a); } catch (FormatException ex) { Console.WriteLine(ex.Message); } catch (Exception exc) { Console.WriteLine(exc.Message); } Console.ReadKey(); } } Az a cath kapja el a hibát (felülről lefele haladva) amely formális param. típusa vagy azonos a kivétel objektum típusával vagy őse annak.
Fejlett kivételkezelés :C# • catch - általános cach • { • Console.WriteLine("Hibásan adta meg a számot!"); • . • . • } • Több catch() esetén a fordító hibát jelez, ha rossz a sorrend
Fejlett kivételkezelés :C# • System.Exception az alkalmazás végrehajtása során elıforduló hibákhoz társított kivételek ősosztálya • System.SystemException a System névtér előre definiált kivételtípusainak ősosztálya • System.ArgumentException egy metódus valamely aktuális paramétere érvénytelen • System.ArgumentNullException null referencia nem megengedett átadása • System.ArgumentOutOfRangeException az átadott paraméter az érvényes tartományon kívülre esik • System.ArithmeticException aritmetikai műveletek és típuskonverzió során előálló kivételek ősosztálya • System.DivideByZeroException nullával történő osztás • System.OverflowException túlcsordulási hiba • System.FormatException a paraméter formátuma nem megfelelő • System.IndexOutOfRangeException tömb túlindexelése • System.IO.IOException fájlkezeléssel kapcsolatos kivételek ősosztálya • System.NotImplementedException a meghívott metódus nem rendelkezik implementációval • System.NullReferenceException egy változón keresztül hivatkozunk egy objektum egy tagjára, és közben a változó null értékű • System.ApplicationException a felhasználó által definiált kivételtípusainak ősosztálya
Fejlett kivételkezelés :C# - kivétel továbbadása class Program { static void Main(string[] args) { string s = "asdfg"; int a = 0; try { a = átalakít(s); Console.WriteLine(a); } catch (Exception exc) { Console.WriteLine(exc.Message); } } public static int átalakít(string s) { return átalakít2(s); } public static int átalakít2(string s) { return int.Parse(s); } }
Kivételkezelés adatbáziskezelő nyelvekben : PLSQL - ORACLE Begin: blokk kezdet, exception : hibaág, when … then : adott hiba kezelése, hiba dobása raise begin select a,b into l_a, l_b from foo where i=1; dbms_output.put_line('a: ' || l_a || ', b: ' || l_b); if (DATE>SYSDATE) then raise date_error endif; exception when date_error then dbms_output.put_line('*** Exc: date_err'); when too_many_rows then dbms_output.put_line('*** Exc: too many rows'); when no_data_found then dbms_output.put_line('*** Exc: no data'); end;
Kivételkezelés adatbáziskezelő nyelvekben : TSQL - MSQL Begin try – end try : a műveleti blokk Begin catch – end catch : a hibakezelő If … : adott hiba kezelése BEGIN TRY ………………. END TRY BEGIN CATCH if (error_number() = 1205) SET …… else SET …….. END CATCH
Kivételkezelés adatbáziskezelő nyelvekben : FOXPRO TRY – ENDTRY CATCH : a hibakezelő WHEN … : adott hiba kezeléseTHROW kivételdobás FINALLY TRY [ tryCommands ] [ CATCH [ TO VarName ] [WHEN lExpression ][ catchCommands ] ] [ THROW [ eUserExpression ] ][ EXIT ] [FINALLY[ finallyCommands ] ] ENDTRY
Egyebek : migrációs segédprogramokSQL Loader • Feladata : Adatok átvitele szövegfájlból adatbázisba • Exceptionfile – t hoz létre • Az állományban azok a rekordok lesznek amiket nem sikerült migrálni
Egyebek : Matlab try /utasítások/catch /utasítások/end A MATLAB nem nyújt lehetőséget sem újrakezdésre, sem ún. final részre.A catch mindent elkap. • Zéróosztás nem ad hibát a =1; b = -5:0.2:4; % ez egy lista 0.2-sével c = 1:0.2:10; f = a./b; % elemenkénti osztás: ./ g = f*c' % skallár szorzás a c transzponáltjával
Összefoglalás • Hasznos eszköz a legtöbb új nyelvben megtalálható • Előnye, hogy jól elkülöníta a feladatokat megoldó kódot a hibakezeléstől. A progr. átláthatóbb, mógosíthatóbb. • Obj. orientált nyelvekben a konstruktor kivételdobással értesítheti a konstruktor hívóját.( arról értesíthet, hogy miért nem) • Hátrányok : kissé elrontja a program strukturáltságát ( hasonló a goto-hoz). Veszélyes, ha olyankor történik kiv. dobás amikor nem szabadna ( C++ destr.) Esetenként többletkód generálódik. • A kivételek előidézése és kezelése jelentős erőforrás igénnyel jár és lassítja az alkalmazás végrehajtását az újabb osztályok betöltése és a veremkezelési feladatok következtében. • Lehetőleg csökkentsük minimális mértékűre az általunk elıődézett kivételek számát. • Kerüljük újabb kivétel elődézését a finally blokkban, ugyanis ha egy kivételt nem fogunk el egyetlen catch blokkal sem, akkor végrehajtódnak a finally blokkban elhelyezett utasítások, és az újabb kivétel elődizése következtében az eredeti elvész. • Amennyiben egy programszakaszon belül több egymás utáni utasításnál is elképzelhető kivétel keletkezése, úgy ne készítsünk ezekhez külön try-catch-finally szerkezeteket, mert nehezen olvashatóvá tennék a kódot. Helyezzük el inkább az érintett utasításokat egyetlen try blokkban, és készítsünk minden kivétel típushoz catch blokkot. .