Изузетак (програмирање)

Из Википедије, слободне енциклопедије

Изузеци у програмирању означавају ситуације у којима одређени програм или неки његов дио не може наставити свој нормалан рад због одређених проблема. Проблеми обично потичу од лоших параметара које задаје корисник, немогућности повезивања са одређеним ресурсом, системских кварова итд.

Хронологија догађаја[уреди]

Неухваћени изузетак „испливава“ до најближег нивоа који који је припремљен за хватање изузетака

Изузеци могу бити различитог поријекла. Сам програмер може да пријави одређену грешку као изузетак, али и различите програмске библиотеке, у зависности од тога коју користимо, пријављују одређене проблеме као изузетке.

Када се такав изузетак деси, он се пријављује програмском блоку вишег нивоа, тј. родитељском блоку, у форми неког податка који га описује. Тако, изузетак се може пријавити у облику текста „Десио се изузетак“, у облику цијелог броја, објекта неке корисничке класе итд. докле год тај податак на добар начин описује споменути изузетак. Обично се каже да је изузетак „бачен“, када се говори о пријављивању изузетка родитељском блоку.

Родитељски блок може да одабере да игнорише било какве изузетке, и у том случају изузетак ће аутоматски бити прослијеђен његовом родитељском блоку. Уколико се, међутим, родитељски блок унапријед припреми за изузетке одређеног типа, каже се да он „хвата“ изузетак и од њега зависи како ће се изузетком руковати.

Уколико ниједан блок не ухвати изузетак, он се пење до првог, почетког програмског блока. Ако га ни он не ухвати, оперативни систем обично реагује насилним прекидањем рада програма; то се обично изводи слањем сигнала ABORT. Сигнал ABORT се може игнорисати, али чак и тада процес остаје у стању поремећене функционалности па се то не препоручује. Из ових разлога се препоручује пажљива обрада изузетака, да би се проблем могао пријавити кориснику и, евентуално, ситуација поправити.

Примјер[уреди]

Нека је задат низ карактера који представљају цифре неког броја. Треба написати функцију која преводи тај низ у цио број чије су то цифре у декадном систему.

При писању ове функције, логично се намеће да ће резултат функције бити цио број, односно да ће функција враћати цио број. Међутим, намеће се проблем како обавијестити позиваоца ако се десила грешка. Уколико одредимо -1 да буде индикатор грешке, онда функција неће моћи да врати тај број као регуларан резултат; заправо, сви цијели бројеви су „покривени“ овом функцијом, и ниједан од њих не можемо искористити као индикатор грешке. У програмским језицима који не подржавају изузетке, овај проблем се обично рјешава постављањем вриједности неке спољашње, глобалне промјенљиве или просљеђивањем показивача или референце на неку промјенљиву чија ће се вриједност поставити на нпр. 1 ако се деси грешка. У програмским језицима који подржавају изузетке, међутим, у датој ситуацији ћемо једноставно бацити изузетак. Слиједи рјешење задатка у псеудокоду:

функција низУБрој(ниска н )

иди на задњу цифру
резултат = 0
степен=1
док не дођемо до прве цифре
ради следеће:
ако је цифра између 0 и 9
онда:
резултат = резултат + степен*цифра
иначе:
баци изузетак
помјери се на претходну цифру
врати резултат

крај функције

главни програм:

низ = "1234"
ризичан блок:
број = низУБрој(низ )
штампај("Резултат је: ", број )
хватај изузетке:
штампај("Десио се изузетак, програм је завршен“ )

крај програма

Дакле, из овог примјера се види да функција баца изузетак када наиђе на неваљану цифру, тј. уопштено говорећи кад год наиђе на услове у којима не може наставити нормалан рад. Главни програм је у овом случају тај који позива ову функцију. Функција која баца изузетке је за програм „потенцијална опасност“ (може бацати изузетке, премда обично не баца, кад су сви услови задовољени), те је ограђујемо у тзв. „ризични блок“ — одмах након ризичног блока слиједи блок који обрађује изузетке. На овај начин се неће десити да нико не „ухвати“ изузетак, што би највјероватније узроковало да програм буде насилно угашен од стране оперативног система.

Синтакса изузетака у језику C++[уреди]

У програмском језику C++, једна од новина у односу на C су били управо изузеци. Потакнути проблемом враћања грешке када је цио опсег излазне промјенљиве заузет потенцијалним резултатом, архитекте C++-а су увеле изузетке као моћно средство обрађивања грешака. Изабрали су кључне ријечи try (енгл. покушати, пробати) за означавање „ризичног“ блока, catch (енгл. хватати) за означавање блока у којем се обрађују изузеци из ризичног блока и throw (енгл. бацити) за бацање изузетака.

Примјер[уреди]

Примјер који смо имали написан у псеудокоду би имао сљедећи облик у C++-у:

#include <iostream>
using namespace std;
 
int nizUBroj(char * niz )
{
    int i;
    int stepen = 1;
    int rezultat = 0;
    for (i = 0; niz[i] != '\0'; i++ )
        ; // да бисмо дошли до знака за крај ниске
 
    i--;  // да бисмо се позиционирали на задњу цифру
    while (i >= 0) {
        if (niz[i] - '0' > 9 || niz[i] - '0' < 0) // ако није у питању једна од нама познатих цифара...
            throw „Низ садржи недозвољене знакове"; // ... баци изузетак
        rezultat += stepen*(niz[i]-'0');
        stepen *= 10;
        i--;
    }
    return rezultat;
}
 
int main()
{
    try { // блок команди у којем се могу појавити изузеци се ограничава кључном ријечју "try"
        cout << nizUBroj("1234") << endl;
    } catch (const char * tekstualniIzuzetak) { // уколико се у блоку "try" баци неки изузетак
                                                  // типа "const char * ", он ће овде бити ухваћен
        cerr << „Грешка: " << tekstualniIzuzetak << endl;
        return 1;
    }
    return 0;
}

У случају да се ниједан изузетак не појави, тј. у случају да су све цифре ваљане, блок catch у главном програму ће бити игнорисан и програм ће се успјешно завршити. Уколико се у блоку try, међутим, баци изузетак, онда ће аутоматски бити прекинуто извршавање цјелокупног блока try и започето извршавање блока catch. То узрокује поруку о грешци и завршавање програма са статусом 1.

Хватање различитих типова изузетака[уреди]

Из ризичног блока могу бити бачени различити типови података. На примјер, одређена функција ће бацати бројеве 1, 2, 3 итд. у зависности од тога која врста грешке се деси. Друга функција, међутим, може бацати ниске „лош аргумент“, „недовољно аргумената“, „дијељење са нулом“, итд. опет у зависности од врсте грешке. Даље, нека трећа функција може бацати изузетке класе string, vector, па чак и неке корисничке класе A, B, Tacka итд. У програмском језику C++ је потребно написати посебне блокове catch за сваки очекивани тип изузетка. Ако се бачени изузетак не поклопи по типу ни са једним од представљених блокова catch, изузетак ће бити игнорисан и послат на ниво изнад.

Примјер[уреди]

Написати функцију за дијељење два броја. Затим написати функцију за рачунање просјека задатог низа. Написати и главни програм који тестира написане функције.

double podijeli(double a, double b )
{
    if (b == 0)   // ако је у питању дијељење са нулом, ово је немогућ задатак...
         throw 1;   // ... па бацамо изузетак (у облику цјелобројне вриједности)
    return a/b;
}
 
double prosjek(double niz[], int n )
{
    if (n == 0)   // ако је број елемената једнак нули, не можемо израчунати просјек...
         throw „Просјек је немогуће израчунати"; // ... па бацамо изузетак, у облику низа карактера
    double suma = 0;
    for (int i = 0; i < n; i++ )
         suma += niz[i];
    return podijeli(suma, n );
}
 
int main()
{
    double niz[] = { 10.0, 0.1, 5.0, 0.5 };
    double pr;
    try {
        pr = prosjek(niz, 4 );
    } catch (const char * tekstualniIzuzetak) { // у овом блоку ће бити ухваћени сви
                                                  // изузеци типа const char *
        cerr << „Грешка: " << tekstualniIzuzetak << endl;
        return 1;
    } catch (int cjelobrojniIzuzetak) {         // у овом блоку ће, међутим, бити ухваћени
                                                  // сви изузеци бачени у облику цјелобројне
                                                  // вриједности.
        cerr << „Грешка бр. " << cjelobrojniIzuzetak << endl;
        return 2;
    }
    return 0;
}

Уколико се деси да се функцији podijeli() неком грешком нареди да дијели са нулом, она ће бацити изузетак. Примијетимо, међутим, да функција prosjek() не хвата изузетке (нема блокова try и catch), те ће евентуални изузетак једноставно бити прослијеђен на виши ниво, тј. функцији main(). Она, међутим, има одговарајући блок catch који хвата изузетке типа int, па ће изузетак бити успјешно обрађен, порука о грешци одштампана и програм завршен са статусом 2.

Синтакса изузетака у Јави[уреди]

Програмски језик Јава користи исти принцип и исте кључне ријечи за изузетке као и C++. Међутим, Јава иде и даље те изузецима поклања посебну пажњу. За разлику од програмског језика C++, Јава има засебну хијерархију класа које могу, и само оне, бити бачене као изузеци. Врх хијерархије ових класа је класа по називу Throwable (буквално, енгл. које може бити бачено), па само објекти те класе, или објекти класа које су посредно или директно изведене из ње, могу бити „бачени“ као изузеци. У Јави постоје бројне класе које су изведене из ове, али и програмер може да насљеђује класу Throwable и баца објекте сопствене класе, која му одговара.

Јава своју „строгост“ по питању изузетака исказује и на друге начине; свака функција која под неким условима баца изузетке, мора бити декларисана као таква. У те сврхе је резервисана кључна ријеч „throws“ (енгл. „баца“), која се користи уз декларацију такве функције, да би описала које све врсте изузетака могу бити бачене из ње. Чак уколико сама функција не баца изузетак, али позива неку која баца а не хвата њене изузетке, декларација функције мора то да описује. Тако, функција у Јави може изгледати на сљедећи начин:

public double prosjek(double [] niz) throws StringException, IntException
{
    // ...
}

Други програмски језици[уреди]

Различити програмски језици вишег нивоа који су своју синтаксу наслиједили од програмских језика C и C++, најчешће су и синтаксу изузетака позајмили из језика C++. Тако, Јаваскрипт, PHP, C#, и други, су сви наслиједили, посредно или непосредно, синтаксу везану за изузетке из језика C++. Поједини, попут Јаве и C#-а су изузетке додатно проширили и уобличили у складу са својом филозофијом.

Спољашње везе[уреди]