nav_dugme codeBlog codeBlog
  • početna Početna stranica
  • Sačuvani članci Sačuvani članci
  • Učionica
  • Saveti
  • Zanimljivosti
  • Kontakt

Izuzeci u programiranju

Viber
zoom_plus zoom_minus bookmark

Uvod

Pedagoški pristup u izučavanju programiranja obično podrazumeva da se u početnim stadijumima ne obraća prevelika pažnja na određene anomalije koje se mogu javiti u toku izvršavanja programa, ukoliko korisnik unese podatke koji dovode u pitanje korektnost izvršavanja programa, ili ako se takvi podaci pojave u toku obrade. Podrazumeva se, u navedenim okolnostima, u kojima je prioritet da se polaznici upoznaju sa osnovnim mehanizmima obrade podataka, da će korisnički unos biti ispravan i da će, samim tim i program uvek vraćati korektan rezultat - naravno, ukoliko je program uopšte dobro napisan.

Međutim, jasno je (još od samog početka), da korisnički unos ne mora biti ispravan, da se tokom izvršavanja programa mogu pojaviti anomalije i da stoga, u opštem smislu, postoji potreba da pravilno odreagujemo u takvim okolnostima.

Recimo, ukoliko pišemo funkciju koja vraća obim pravougaonika (za unete stranice a i b), sve će biti u redu ukoliko unesemo vredosti kao što su 3.5 i 7.2, međutim, šta ako smo uneli 12.4 i 0, ili 5 i -5?

U prvom slučaju, dobićemo rezultat (24.8) koji deluje dobro (ali, do koga smo došli pod "sumnjivim" okolnostima), dok će u drugom slučaju i sama vrednost koju funkcija vraća (0) biti krajnje diskutabilna.

U navedenim (kao i mnogim drugim) okolnostima, dobro dođe mehanizam koji proverava unete vredsnoti, obustavlja dalje izvršavanje ukoliko vrednosti nisu pravilno unete i naravno - obaveštava programere (a po potrebi i krajnje korisnike) o tome da je do grešaka došlo.

Pojam izuzetka i kraća diskusija o situacijama koje se mogu rešiti bez upotrebe izuzetaka

Pre nego što se detaljnije pozabavimo izuzecima (koji su glavna tema članka), razmotrićemo, preko jednog jednostavnog primera, da postoje i situacije u kojima nisu potrebni izuzeci da bismo dobili sve informacije o obradi koja je izvršena (korektan rezultat, ili poruku o grešci, u zavisnosti od ulaznih podataka).

Da pomenemo za svaki slučaj: radi se isključivo o okolnostima kada se svi granični slučajevi mogu 'pokriti' bez upotrebe izuzetaka.

Funkcija za pronalaženje prve pojave niske s2 unutar niske s1, verovatno je najuobičajeniji primer funkcije koja se može implementirati bez izuzetaka.

		
int poklapanjeNiski(string s1, string s2)
{

}
		
	
Slika 1. - Osnovni "okvir" funkcije za proveru poklapanja dve niske.

Ukoliko se niska s2 pojavljuje unutar niske s1, funkcija će vratiti indeks prvog poklapanja: na primer, za niske "Beograd" i "grad", funkcija vraća 3, dok u slučaju niski "Beograd" i "Beo", funkcija vraća 0.

U pitanju su očekivane vrednosti za situacije kada dolazi do poklapanja, međutim, šta funkcija treba da vrati ukoliko poklapanja nema (s obzirom da je povratni tip funkcije celobrojna vrednost)?

Uz (sasvim) malo promišljanja i snalaženja, lako ćemo zaključiti da je dovoljno da u navedenim okolnostima vratimo bilo koju negativnu vrednost (tipično -1).

Takva vrednost (vrlo očigledno) ne predstavlja poziciju u nizu znakova, pa je stoga (kao takva) svojevrstan "signal" da do poklapanja nije došlo.

Funkciju sada možemo implementirati na jednostavan način (doduše, prikazaćemo samo šematski prikaz; konkretnu implementaciju koja podrazumeva korišćenje nekog od algoritma za proveru poklapanja niski, ovde ćemo, zarad preglednosti, ipak izostaviti):

		
int poklapanjeNiski(string s1, string s2)
{
	int p = -1;

	/* -- OBRADA -- */

	// Ukoliko pronadjemo poklapanje,
	// p će dobiti vrednost indeksa
	// na kome poklapanje počinje,
	// dok će u suprotnom zadržati
	// početnu vrednost

	return p;

}
		
	
Slika 2. - Šema funkcije za proveru poklapanja dve niske. Početna vrednost indeksa, -1, promeniće se ukoliko se pronađe poklapanje, dok će u suprotnom ostati (i služiti kao "signal" da do poklapanja nije došlo).

Sa druge strane, postoje (reklo bi se znatno brojniji) primeri gde prikazani pristup ne možemo koristiti, a verovatno najtipičniji primer (koji čitaoci mogu razumeti čak i ukoliko nemaju previše iskustva sa bazama podataka), predstavljaju funkcije za povezivanje sa bazama podataka na udaljenom serveru (dostupne u brojnim programskim jezicima; većina programera ih koristi gotovo svakodnevno).

Funkcije za povezivanje sa bazom obično imaju četiri parametra: ime servera, korisničko ime za pristup serveru, lozinku i ime baze, a za povratnu vrednost imaju objekat preko koga se ostvaruje veza sa bazom (i nadalje, čitanje i obrada podataka).

Naravno, ukoliko su parametri korektno predati i veza uspešno uspostavljena; u suprotnom - funkcija vraća izuzetak.

Izuzetak je specijalno formatirana poruka koja sadrži informaciju o tome da je došlo do greške, kao i (što je važnije) - dodatne informacije o prirodi greške.

U prethodnom primeru (povezivanje sa bazom), uz korišćenje izuzetaka - ukoliko dođe do greške - dobijamo informaciju o tome šta je tačno "krenulo naopako" pri povezivanju (da li je predato pogrešno korisničko ime, ili naziv baze, ili se desilo nešto sasvim treće), što je svakako mnogo bolje nego da nam program samo saopšti da "nešto" (pri čemu ne bismo znali šta konkretno), nije u redu, ili - što bi bilo mnogo gore (ali, što se ipak retko sreće u pravilno napisanim programima) - da nam program uopšte ni ne sopšti da je do greške došlo!

U nastavku, bavićemo se izuzecima (s tim što ćemo se vratiti na jednostavniji primer sa obimom pravougaonika, koji lako možete isprobavati samostalno).

Kreiranje izuzetaka

Kreiranje izuzetaka nije ni izdaleka komplikovana procedura, pa ćemo pogledati odmah jednostavan primer upotrebe.

Kreiranje izuzetaka u programskom jeziku C++

Ukoliko dođe do greške, program, preko funkcije throw, prosleđuje poruku o grešci:

		
double racunanjeObima(double a, double b)
{
	if (a < 0) {
		throw "Dužina stranice a ne sme biti negativan broj.";
	}

	if (a == 0) {
		throw "Dužina stranice a ne sme biti 0.";
	}

	if (b < 0) {
		throw "Dužina stranice b ne sme biti negativan broj.";
	}

	if (b == 0) {
		throw "Dužina stranice b ne sme biti 0.";
	}

	return 2 * a + 2 * b;
}
		
	
Slika 3. - Kreiranje jednostavnih izuzetaka u tekstualnom obliku, preko naredbe throw.

Međutim, ukoliko pozovemo sledeći kod:

		
double obim = racunanjeObima(-2.32, 12.2);
cout << "Da li vidimo ovaj tekst?!";
		
	
Slika 4. - Provera funcionalnosti prethodno definisane funkcije sa izuzecima.

.... na ekranu se neće pojaviti ni jedna od prethodno navedenih poruka (koje smo definisali u funkciji racunanjeObima), kao ni poruka "Da li vidimo ovaj tekst?!".

Kada program naiđe na izuzetak za koji nije predviđena odgovarajuća obrada - jednostavno će "pući" (da budemo precizni, prestaće sa izvršavanjem).

Blok try-catch - Obrada izuzetaka

Da bi izuzeci koje smo definisali (u funckiji racunanjeObima, a naravno i inače), mogli da se koriste na pravi način, potrebno je koristiti blok try-catch.

U pitanju je svojevrsno grananje u programu, sa sintaksom nalik na standardni blok if-else, sa kojim ste se sretali mnogo puta do sad.

Pogledajmo primer:

		
double a, b, obim;

try {
	// 1.
	a = 4.5;
	b = 6.2;

	cout << "Stranice pravougaonika: " + a + " i " + b << endl;
	obim = racunanjeObima(a, b);
	cout << "Obim: " << obim << endl;

	// 2.
	a = -3.32;
	b = 2.6;

	cout << "Stranice pravougaonika: " + a + " i " + b << endl;
	obim = racunanjeObima(a, b);
	cout << "Obim: " << obim << endl;

	// 3.
	a = 4.2;
	b = 6.4;

	cout << "Stranice pravougaonika: " + a + " i " + b << endl;
	obim = racunanjeObima(a, b);
	cout << "Obim: " << obim << endl;

}
catch (const char* greske) {
	cout << "Pri računanju obima pravougaonika, ";
	cout << "došlo je do sledećih grešaka:" << endl;
	cout << greske;
	cout << "--DODATNA OBRADA!--" << endl;
}
		
	
Slika 5. - Blok try-catch, kao mehanizam za "hvatanje izuzetaka" (u prethodnom pozivu smo videli da se izuzeci, iako smo ih u funkciji koju pozivamo, nisu pojavili u toku izvršavanja programa na očekivani način).

Ovoga puta, program se izvršava na sledeći način:

		
Stranice pravougaonika: 4.5 i 6.2
Obim: 21.4
Stranice pravougaonika: -3.32 i 2.6
Pri računanju obima pravougaonika, 
došlo je do sledećih grešaka:
Dužina stranice a ne sme biti negativan broj.
--DODATNA OBRADA--

--------------------------------
Process exited after 0.0728 seconds with return value 0
Press any key to continue . . .
		
	
Slika 6. - Izvršavanje programa koji koristi try-catch blok.

Blok try-catch je specifično namenjen obradi izuzetaka i funkcioniše na sledeći način:

  • u odeljku try, navode se opšte naredbe obrade podataka ("ono što treba da se desi", ukoliko ne dođe do grešaka)
  • u odeljku catch, navode se naredbe koje treba izvršiti u slučaju da do grešaka dođe
  • ukoliko ni u jednom pozivu unutar bloka try ne dolazi do grešaka, blok catch se (uopšte) neće izvršavati
  • ukoliko bilo koja od naredbi iz bloka try vrati izuzetak, momentalno prestaje izvršavanje naredbi iz bloka try (pojava naredbe throw je u tom smislu veoma slična pojavi naredbe return) i prelazi se na odeljak catch

Primer koji smo videli, sledi navedena pravila:

  • blok try podeli smo na tri (gotovo) identična dela
  • u svakom od delova nalaze se dve naredbe dodele (za stranice a i b), naredba ispisa za stranice, poziv metode racunajeObima i, na kraju, naredba ispisa za obim
  • prvi blok naredbi izvršava se na očekivan način
  • u drugom bloku, dolazi do naredbi dodele, prva naredba ispisa se takođe izvršava, ali - poziv funkcije racunanjeObima generiše izuzetak!
  • izvršavanje bloka try se momentalno prekida i prelazi se na izvršavanje bloka catch

Blok catch ispisuje:

  • opštu poruku koju smo naveli
  • tekst izuzetka koji je definisan u funckiji racunanjeObima
  • drugu opštu poruku koju smo naveli

Poruka "--DODATNA OBRADA--", koju ste mogli videti, sugeriše da je (naravno), u situacijama kada se izuzeci pojave (a "uhvatimo" ih preko bloka catch), moguće napisati naredbe koje će, po potrebi, program usmeriti na odgovarajući način (zahtevati novi unos podataka, obustaviti izvršavanje programa ili nešto treće).

Kompleksniji primer izuzetka sa više poruka

U prethodnom slučaju, kreirali smo funkcionalan mehanizam provere koji obustavlja dalje izvršavanje programa i vraća poruku o grešci. Međutim, ovakav mehanizam ima nekoliko nedostataka:

  • nećemo biti obavešteni o svim greškama, već samo o prvoj grešci koja se pojavi
  • program prekida izvršavanje čim naiđe na pravougaonik sa neodgovarajućim stranicama

.... što ostavlja mesta za dalja uapređenja.

Drugi navedeni nedostatak lako možemo rešiti kreiranjem zasebnih try-catch blokova za svaki pravougaonik (prethodno smo stavili sva tri pravougaonika u isti blok, da bismo prikazali osnovni način funkcionisanja try-catch bloka), dok ćemo za ispis svih poruka morati malo više da se potrudimo.

Kao što u strujnim instalacijama postoje spori i brzi osigurači (prvi štite robusne industrijske uređaje koji imaju izvesnu toleranciju prema pojavi struja većih od dozvoljenih, dok drugi štite relativno osetljive uređaje kakve srećemo u domaćinstvima, koji nemaju preveliku toleranciju prema pojavi struja i napona većih od dozvoljenih), tako i, kada su izuzeci u pitanju, razlikujemo dve situacije:

  • prvu, u kojoj je bitno da što pre prekinemo dalje izvršavanje, čim se pojavi bilo kakava anomalija u funkcionisanju programa (da time prekinemo dalje "rasipanje" resursa)
  • drugu, u kojoj je najbitnije da prikupimo što više informacija o okolnostima koje su dovele do nepravilnog funkcionisanja programa (da se greške ne bi ponavljale u budućnosti i sl)

Naravno, nije sve tako "crno i belo" i postoji veliki broj "međuslučajeva", ali, u nastavku ćemo izuzetke definisati na sledeći način: zanima nas koji sve parametri pravougaonika nisu bili dobro navedeni.

Funkcija za unos novog sloga u tabelu baze podataka, koja nas preko izuzetaka obaveštava o svim slogovima koji nisu dobro uneti - a ne samo o tome da je do greške došlo, svakako bi bila zanimljiviji primer, međutim, zadržaćemo se i ovde na pojednostavljenom, "šematskom" primeru koji čitaoci lako mogu isprobati sami.

Za početak, pripremićemo klasu preko koje se mogu skladištiti sve informacije o izuzecima:

		
#include<vector<

class Izuzetak
{
	private:
		
	int            BrojIzuzetaka;
	vector<string> SpisakPoruka;
	bool           ImaIzuzetaka;
	
	public:
		
	Izuzetak()
	{
		ImaIzuzetaka  = false;
		BrojIzuzetaka = 0;	
	}

	bool ImaLiIzuzetaka()
	{
		return ImaIzuzetaka;
	}
	
	void GenerisanjeIzuzetka(string s)
	{
		SpisakPoruka.push_back(s);
		BrojIzuzetaka++;
		ImaIzuzetaka = true;	
	}
	
	string IspisIzuzetaka()
	{
		string s = "";
		
		for (int i = 0; i < this->BrojIzuzetaka; i++) {
			s += SpisakPoruka[i];
		}
				
		return s;
	}	
};
		
	
Slika 7. - Klasa Izuzetak preko koje možemo vraćati kompleksnije izuzetke sa više informacija o greškama koje su se desile u obradi.

Detaljniju analizu klase ostavljamo vama, ali, napomenućemo da su sva polja privatna (kao što i treba da bude u ovakvoj klasi) i da se njihova vrednost menja isključivo preko metode GenerisanjeIzuzetaka (u suprotnom, moglo bi doći do povećih nepravilnosti).

Sada možemo prepraviti metodu za računanje obima:

		
double racunanjeObima(double a, double b)
{
	Izuzetak izuzetak;
	
	if (a < 0) {
		string s = "Duzina stranice a ne sme biti negativan broj.\n";
		izuzetak.ImaIzuzetaka = true;
		izuzetak.GenerisanjeIzuzetka(s);
	}

	if (a == 0) {
		string s = "Duzina stranice a ne sme biti 0.\n";
		izuzetak.ImaIzuzetaka = true;
		izuzetak.GenerisanjeIzuzetka(s);
	}

	if (b < 0) {
		string s = "Duzina stranice b ne sme biti negativan broj.\n";
		izuzetak.ImaIzuzetaka = true;
		izuzetak.GenerisanjeIzuzetka();
	}

	if (b == 0) {
		string s = "Duzina stranice a ne sme biti 0.\n";
		izuzetak.ImaIzuzetaka = true;
		izuzetak.GenerisanjeIzuzetka();
	}
	
	if (izuzetak.ImaIzuzetaka) {
		throw izuzetak;
	}

	return 2 * a + 2 * b;
}
		
	
Slika 8. - Funkcija racunanjeObima, prepravljena tako da koristi klasu Izuzetak.

Kao što smo i naveli, dosledno skupljamo sve informacije, pre nego što metoda generiše izuzetak (naravno, ako za tim ima potrebe).

Sada će i catch odeljak biti nešto drugačiji ....

		
try {
	....
}
catch (Izuzetak& izuzeci) {
	cout << "Pri racunanju obima pravougaonika, ";
	cout << "doslo je do sledecih gresaka:" << endl;
	cout << izuzeci.IspisIzuzetaka();
	cout << "--DODATNA OBRADA--" << endl;
}
		
	
Slika 9. - Ispis pronađenih grešaka, preko metode IspisIzuzetaka, definisane u klasi Izuzetak.

(.... u tom smislu da ćemo pozivati metodu za ispis, umesto da "hvatamo" i ispusjemo niske koje je funkcija racunanjeObima vratila kao izuzetak.)

Sada, kada razumemo da funkcije mogu vraćati izuzetke koji su objekti klasa, pogledaćemo još jednu šemu.

Blok try-catch sa višestrukim catch odeljkom

Vraćajući se na primer sa povezivanjem sa bazom, pogledaćemo šemu koja predstavlja razgranati try-catch blok koji bismo mogli upotrebiti u ovakvoj situaciji:

		
try {
	// Pokušaj povezivanja
	// sa bazom
}
catch(IzuzetakServer izuzetakServer) {
	// Ispis informacija o grešci
	// pri povezivanju sa serverom
}
catch(IzuzetakKorisnik izuzetakKorisnik) {
	// Ispis informacija o grešci
	// pri unosu korisničkog imena
	// i/ili lozinke (pogrešno ime,
	// pogrešna lozinka i sl.)
}
catch(IzuzetakBaza izuzetakBaza) {
	// Ispis informacija o grešci
	// pri povezivanju sa bazom
	// (pogrešan naziv baze, korisnik
	// nema pravo pristupa bazi i sl.)
}
		
	
Slika 10. - Blok try-catch sa višetrukim catch odeljcima, preko kojih možemo "hvatati" vrlo specifične izuzetke (od kojih svaki možemo definisati preko posebne klase).

Dakle, možemo nadovezivati catch blokove i udestiti da svaki od njih bude namenjen obradi specifičnog tipa izuzetka.

I naravno, svaku od navedenih klasa (IzuzetakServer, IzuzetakKorisnik i IzuzetakBaza) bilo bi potrebno definisati unapred (to ćemo ovoga puta izostaviti), ali, jasno je da navedeni pristup omogućava visok nivo fleksibilnosti u radu sa izuzecima (naravno, uz neizostavno povećanje kompleksnosti programskog koda).

Za kraj ....

U ovom članku dotakli smo se najvažnijih tehnika vezanih za izuzetke i nadamo se da sada imate bolju predstavu o tome šta je potrebno učiniti ukoliko postoji mogućnost da funkcionalnost određenih delova programskog koda bude narušena zbog pogrešnog korisničkog unosa ili drugih okolnosti (dok, ukoliko takva mogućnost doslovno ne postoji, treba biti racionalan i ne treba trošiti vreme na kreiranje delova koda koji geerišu i obrađuju zadatke).

Za vežbu, probajte (na primer) da sami implementirate klasu Izuzeci, specifično namenjenu obradi grešaka do kojih može doći u radu sa ulančanim listama, o kojima smo govorili u prethodnom članku.

Autor članka Nikola Vukićević Za web portal www.codeblog.rs
Napomena: Tekstovi, slike, web aplikacije i svi ostali sadržaji na sajtu www.codeblog.rs (osim u slučajevima gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta www.codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog pismenog odobrenja autora.
© 2020-2023. Sva prava zadržana.
Viber
početna Početna > Članci > Izuzeci u programiranju

Info & povezani članci Info o članku - dugme

Info

trejler_sat Datum objave: 21.01.2021.

trejler_sat Poslednja izmena: ----

trejler_dokument Jezici: C++

trejler_teg_narandzasti Težina: 6/10

Povezani članci

Objektno orijentisano programiranje Strukture podataka Klase složenosti algoritama - (O notacija) Regularni izrazi - napredna pretraga teksta Uvod u relacione baze podataka i SQL AVL Stablo - Implementacija - 1. deo - Osnovna struktura Shunting Yard - Implementacija - 1. deo - Prevođenje izraza iz infiksnog u postfiksni zapis Operacije sa nizovima u programskom jeziku JavaScript Tutorijal - Implementacija binarnog stabla pretrage u programskom jeziku JavaScript Tutorijal: Implementacija jednostruko ulančane liste u programskom jeziku C++ Ostali članci
In programming,the hard part isn't solving problems, but deciding what problems to solve.
Paul Graham
codeBlog codeBlog
Projekat posvećen popularizaciji kulture i veštine programiranja.
Napomena: Tekstovi i slike na sajtu www.codeblog.rs (osim u slučajevima, gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta www.codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog odobrenja autora.
© 2020-2023. Sva prava zadržana.
Facebook - logo
Instagram - logo
LinkedIn - logo
Twitter - logo
E-mail
Naslovna
   •
Uslovi korišćenja
   •
Obaveštenja
   •
FAQ
   •
Kontakt