appunti3s:eccezioni
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
appunti3s:eccezioni [2018/05/03 22:30] – profpro | appunti3s:eccezioni [2020/06/08 22:19] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | > indice > [[appunti3s: | ||
+ | ====== Gestione delle eccezioni ====== | ||
+ | |||
+ | ===== Premessa ===== | ||
+ | Quando il programmatore deve scrivere un programma spesso incontra errori di diverso tipo: | ||
+ | - errori di compilazione | ||
+ | - errori di linking | ||
+ | - errori di esecuzione (run-time) | ||
+ | - errori di correttezza dei risultati | ||
+ | I primi due tipi di errori impediscono la generazione del programma, ma anche un programma che sembra scritto senza errori può avere un comportamento indesiderato durante l' | ||
+ | |||
+ | Il terzo tipo di errori può rientrare nella categoria dei cosiddetti " | ||
+ | |||
+ | Il quarto tipo è detto anche errori di logica. È meno grave del precedente perché il programma riesce almeno a produrre sempre dei risultati, solo che i risultati non sono sempre corretti. A volte sono causati dalle richieste dell' | ||
+ | ===== Confronto tra C e C++ ===== | ||
+ | |||
+ | In C si controllava il buon esito di una funzione controllando il valore restituito (0 = nessun errore). In caso di segnalazione di un errore (restituzione di valore non zero) in C si chiamava la funzione //exit()// oppure //abort()// lasciando gli eventuali oggetti dinamici allocati in memoria. | ||
+ | Per evitare questo inconveniente ed usare un sistema ben organizzato, | ||
+ | |||
+ | In ogni caso, indipendentemente dalla strategia usata, il programmatore che vuole minimizzare gli errori, cerca di prevenirli usando strumenti con il // | ||
+ | ===== Quando si usano le eccezioni ===== | ||
+ | |||
+ | Le eccezioni sono oggetti (di tipo classe) che, quando un problema, vengono creati e passati come il parametro di una funzione. A volte capita che l' | ||
+ | Gli oggetti di tipo eccezione possono contenere informazioni, | ||
+ | |||
+ | Ci sono due tipi di programmatori: | ||
+ | |||
+ | Grazie alle eccezioni queste due attività si distinguono ancora meglio e scrivere il codice diventa ancora più facile: | ||
+ | * il programmatore che scrive il codice delle funzioni indica agli altri (nelle dichiarazioni delle funz.) solo quale tipo di eccezioni potrebbero essere lanciate (e quando). | ||
+ | * il programmatore che usa una funzione scritta da qualcun altro indica (nelle chiamate delle funz.) cosa deve fare il suo programma quando dovesse ricevere quel tipo di eccezione. | ||
+ | Ad esempio, nel caso in cui un programma non riesca a leggere il file specificato dall' | ||
+ | Lanciare eccezioni è un sistema comodo per chi scrive, ma anche leggermente poco efficiente perché richiede anche di dover ripercorrere (srotolare) lo stack delle chiamate delle funzioni: un' | ||
+ | |||
+ | ===== Definizioni ===== | ||
+ | |||
+ | |||
+ | - Le funzioni che possono generare eccezioni devono contenere le condizioni (//if//) per individuare la presenza di un problema. | ||
+ | - L' | ||
+ | - <file c letturaPeso.h> | ||
+ | - <file c letturaPeso.cpp> | ||
+ | void letturaPeso(int& | ||
+ | { | ||
+ | std::cout << " | ||
+ | std::cin >> n; | ||
+ | if ( n < 0 ) | ||
+ | throw (-1); // viene lanciato un intero | ||
+ | }</ | ||
+ | - Chi usa queste funzioni deve racchiuderle dentro un blocco // | ||
+ | - Il blocco //try// è seguito da uno o più blocchi // | ||
+ | - <file c main.cpp> | ||
+ | #include < | ||
+ | #include " | ||
+ | int main() | ||
+ | { int peso; | ||
+ | try { | ||
+ | letturaPeso(peso); | ||
+ | } | ||
+ | | ||
+ | | ||
+ | if ( k=-1 ) | ||
+ | std::cerr << "Hai inserito un peso negativo..." | ||
+ | return 0; // si decide di terminare il main | ||
+ | } | ||
+ | | ||
+ | | ||
+ | }</ | ||
+ | - Quando la funzione (usata dentro //try//) trova il problema, " | ||
+ | - // | ||
+ | - Dentro le parentesi tonde di ogni blocco //catch()// sarà indicato (come un parametro intero k) il tipo di eccezione che quel blocco sa " | ||
+ | - Il blocco catch dà la possibilità di chiudere il programma o di provare a ripetere l' | ||
+ | - Dopo il blocco //try// ci possono essere uno o **più** blocchi //catch()// ognuno dei quali è specializzato nel ricevere un particolare tipo di parametro. Il controllo dell' | ||
+ | - Durante l' | ||
+ | - Per il punto precedente, quindi, il parametro passato, non dovrebbe essere un puntatore ad una variabile locale < | ||
+ | - Sempre per la stessa ragione, un distruttore non deve mai generare un' | ||
+ | - Il passaggio di parametri avviene sempre per copia quindi potrebbe essere invocato un costruttore di copia, a sua volta possibile fonte di ulteriori eccezioni, che non si possono annidare... Quindi è meglio usare i //const reference// | ||
+ | - Al termine di un try-senza-catch o di un try-con-catch, | ||
+ | |||
+ | {{: | ||
+ | |||
+ | ===== Re-throw ===== | ||
+ | Il blocchi //try// possono a loro volta contenere altri blocchi //try// all' | ||
+ | |||
+ | {{: | ||
+ | |||
+ | Se non viene trovato nessun catch che gestisce l' | ||
+ | ===== Throw list ===== | ||
+ | Quando una funzione contiene il " | ||
+ | Dentro le parentesi di questo throw() va indicata la lista dei tipi di eccezioni che possono essere " | ||
+ | |||
+ | ===== Catch generico ===== | ||
+ | Alla fine della lista dei gestori //catch()// può essere utile inserire un blocco //catch()// che raccolga tutto quello che non è stato ricevuto dai suoi predecessori. Tale gestore deve avere la seguente sintassi: | ||
+ | |||
+ | < | ||
+ | </ | ||
+ | Tuttavia questo sistema rischia di catturare anche eccezioni impreviste. La cosa migliore è far derivare tutte le eccezioni dalla classe base // | ||
+ | |||
+ | < | ||
+ | |||
+ | Questo catch generico è spesso controproducente perché catturano delle eccezioni che potrebbero essere gestite ad un livello più esterno. Esistono delle eccezioni che non sono di stretta competenza della funzione dove si è generata l' | ||
+ | |||
+ | Se //throw;// viene usato nel catch generico, allora quel catch generico potrebbe essere inutile. | ||
+ | Scrivere un altro blocco //catch()// dopo il catch generico è inutile perché quest' | ||
+ | ===== Standard exception===== | ||
+ | |||
+ | Che oggetto lanciare come eccezione? Si potrebbe lanciare anche un tipo primitivo (int) ma lanciare un oggetto permette di far passare un numero maggiore di informazioni sull' | ||
+ | Esistono degli appositi oggetti creati per questo scopo. In italiano si potrebbero chiamare come " | ||
+ | Per poterne usufruire si deve: | ||
+ | * includere l' header //< | ||
+ | * usare namespace // | ||
+ | |||
+ | Poiché le eccezioni sono delle classi, tra loro esiste una gerarchia e si possono utilizzare tutti gli strumenti offerti dal polimorfismo: | ||
+ | |||
+ | ^ header^ classe ^ descrizione ^ | ||
+ | | <new> | std%%:: | ||
+ | | < | ||
+ | | < | ||
+ | | ??? | std%%:: | ||
+ | | ??? | std%%:: | ||
+ | |||
+ | la classe // | ||
+ | |||
+ | ^ header^ classe ^ descrizione ^ | ||
+ | | ??? | std%%:: | ||
+ | | ??? | std%%:: | ||
+ | | ??? | std%%:: | ||
+ | | ??? | std%%:: | ||
+ | |||
+ | la classe // | ||
+ | |||
+ | ^ header^ classe ^ descrizione ^ | ||
+ | | ??? | std%%:: | ||
+ | | ??? | std%%:: | ||
+ | | ??? | std%%:: | ||
+ | |||
+ | |||
+ | ===== Regole pratiche ===== | ||
+ | - tutti i dati che vengono dall' | ||
+ | - ????meglio non usare //try// per racchiudere la chiamata di una funzione, ma usare //try// nel corpo della funzione da chiamare (altrimenti catch non riuscirebbe a distruggere gli oggetti creati dalla funzione) | ||
+ | - racchiudere //new// dentro //try// (// | ||
+ | - se un blocco //try// viene dopo un //new//, allora il relativo //catch()// deve eseguire prima di tutto //delete// (in ordine inverso) e poi rilanciare re-throw (vedere definizione n.7) | ||
+ | - racchiudere //return oggetto;// dentro //try// perché potrebbe generare eccezione di memoria...??? | ||
+ | - racchiudere le inizializzazioni e le chiamate dei costruttori dentro //try// (vedi regola 0) | ||
+ | - se un blocco //try// __crea__, __copia__, o modifica un oggetto, allora sarebbe stato meglio lavorare su una copia di tale oggetto e solo dopo trasferire i dati all' | ||
+ | - se __distrugge__ un oggetto non deve generare mai eccezioni. | ||
+ | - tipi primitivi, references e puntatori non generano eccezioni (esempio??? | ||
+ | - usare per le classi di eccezioni l' | ||
+ | - se due funzioni devono essere eseguite in sequenza, eseguire prima la più critica. | ||
+ | - se non si può modificare l' | ||
+ | - usare sempre Resource Aquisition Is Inizialisation ([[appunti3s: |
appunti3s/eccezioni.txt · Last modified: 2020/06/08 22:19 by 127.0.0.1