Gestione degli errori

La gestione degli errori è un elemento molto importante per la buona riuscita di un software di qualità. A nessuno piace ricevere messaggi di errore incomprensibili, con conseguenti comportamenti inattesi e magari anche la possibilità di perdere del lavoro già fatto.

La presenza di bug logici, che non vengono segnalati in fase di compilazione, è quasi inevitabile, o comunque molto difficile da evitare, ed è quindi molto importante prepararsi a gestire eventuali errori logici.

Quando il nostro programma esegue delle istruzioni che generano dei problemi, o non sono consentite o supportate, o comunque vanno in errore, viene generata una “eccezione”. Una eccezione è appunto un “evento” che notifica un problema accaduto in una qualsiasi funzione, il quale viene propagato a ritroso alla funzione che ha chiamato quella funzione, e cosi via fino alla funzione in testa a tutte (ad esempio il main).

In c++, come in molti altri linguaggi, esiste uno strumento per intercettare queste eccezioni, che è il costrutto try-catch.

Se abbiamo un blocco di codice di cui vogliamo intercettare eventuali eccezioni, possiamo quindi inserirlo in un try-catch in questo modo:

try

{

// istruzioni

}

catch(exception ex)

{

//codice in risposta dell’eccezione

}

In questo modo, se nel blocco di codice racchiuso nel try viene generata un’eccezione, l’esecuzione salta direttamente al blocco di codice contenuto nel “catch”, mettendo a disposizione un oggetto che contiene informazioni riguardo all’errore, come ad esempio il messaggio, un eventuale id, informazioni sullo stato ecc.

Potremmo ad esempio inserire del codice che mostri un messaggio user friendly tipo ” si è verificato un errore, contattare l’assistenza per ottenere un supporto”, piuttosto che un errore incomprensibile per l’utente tipo “Fatal error 0x03432 in system32.dll”

I tipi di eccezione sono tanti, tutti derivati dalla classe “exception”, ed ognuno è specifico al tipo di errore verificatosi. Esistono ad esempio eccezioni per deferenziazioni a puntatori nulli (il famoso null reference pointer”), o argomenti nulli passati a una funzione, o a errori di accesso in lettura o scrittura agli stream, e cosi via.

Può verificarsi anche il caso in cui uno stesso blocco di codice possa generare più tipi di eccezioni, e che noi vogliamo definire dei comportamenti diversi a seconda dell’eccezione generata.  Per farlo possiamo inserire più blocchi catch, specificando per ciascuno il tipo di eccezione che vogliamo intercettare.

E’ importante fare attenzione all’ordine in cui si inseriscono i blocchi catch, perchè essendo l’esecuzione del codice sequenziale, alcuni blocchi catch potrebbero essere esclusi. Vediamo un esempio:

try

{
//istruzioni
}

catch(exception ex)

{ // istruzioni di gestione dell’errore generico }

catch(overflow_error ex)

{//codice eseguito in seguito ad errore di tipo overflow_error }

 

In questo blocco try-catch abbiamo due blocchi catch, uno per gestire un’eccezione generica (exception) e uno per gestire un’eccezione di tipo overflow_error.

Se viene generata un’eccezione generica, viene eseguito il codice corrispondente al blocco catch(exception ex), e tutto va come previsto. Se invece si solleva un’eccezione di tipo overflow_error, siccome il blocco catch che intercetta le eccezioni generiche si trova più in alto, e siccome overflow_error è in effetti una classe ereditata da exception, ed è quindi anch’essa assimilabile al tipo exception, il codice eseguito è di nuovo quello del primo catch.

Invertendo l’ordine dei catch, ovvero mettendo l’eccezione più specifica in alto e quella più generica in basso, ciascun catch intercetterà soltanto le eccezioni del tipo specifico indicato, o di tipi ereditati da esso.

try

{
//istruzioni
}

catch(overflow_error ex)

{//codice eseguito in seguito ad errore di tipo overflow_error }

catch(exception ex)

{ // istruzioni di gestione dell’errore generico }

 

Possiamo sollevare noi stessi eccezioni in particolari situazioni, in seguito a dei controlli tali per cui riteniamo che l’esecuzione non possa continuare. Se ad esempio stiamo scrivendo una funzione che divide due numeri, dovremmo controllare che il divisore non sia zero. In tal caso, possiamo sollevare un’eccezione, del tipo che riteniamo più opportuno:

float Dividi(float a, float b)

{

if(b==0)

{ throw exception(“divisione per zero non consentita”); }

return a/b;

}

con throw tipoeccezione(parametri) generiamo un’eccezione del tipo specificato, usando uno dei costruttori messi a disposizione dal tipo specifico di eccezione. Essa verrà propagata a ritroso nella pila di chiamate a funzione fino alla prima, a meno che in una delle funzioni chiamate a cascata ci sia un blocco try-catch del tipo uguale all’eccezione da noi sollevata, o di un tipo da cui essa eredita.

Se invece ci accorgessimo che non esiste un tipo di eccezione che coincide esattamente con le nostre esigenze, possiamo crearne una, ereditando dal tipo più generico exception o da un’eccezione simile a quella che ci serve, estendendola a nostra discrezione.

E’ nostra cura specificare i tipi di eccezione sollevati dai metodi che scriviamo. Per il caso precedente, nell’header della nostra classe scriveremo ad esempio:

float Dividi(float a, float b) throw(exception, miaccezione)

in questo caso chi utilizzerà la nostra funzione, saprà che il metodo può generare due tipi di eccezioni, ovvero excepion e miaeccezione, e adeguerà il suo codice con eventuali blocchi try-catch, se lo riterrà necessario.

 

Annunci
  1. Lascia un commento

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...

%d blogger hanno fatto clic su Mi Piace per questo: