appunti3s:gestione_delle_eccezioni
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
appunti3s:gestione_delle_eccezioni [2018/05/03 22:26] – profpro | appunti3s:gestione_delle_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. | ||
+ | |||
+ | ===== Confronto tra C e C++ ===== | ||
+ | |||
+ | In C si controllava il buon esito di una funzione controllando il valore restituito (0 = nessun errore). In C si chiamava //exit()// oppure //abort()// lasciando eventuali oggetti dinamici allocati in memoria. | ||
+ | Per evitare questi problemi e usare un sistema ben organizzato, | ||
+ | |||
+ | Indipendentemente dalla strategia usata, il programmatore che vuole minimizzare gli errori, cerca di prevernirli usando strumenti con il // | ||
+ | ===== Quando si usano le eccezioni ===== | ||
+ | |||
+ | Le eccezioni sono oggetti (di tipo classe) che sono generati quando c'è qualcosa che non va, come quando cerco di mettere un tipo di dato in una variabile di un tipo diverso. | ||
+ | Questi oggetti possono contenere informazioni riguardanti il tipo di errore. Un errore è gestito con le eccezioni quando quel tipo di errore non è frequente. | ||
+ | |||
+ | Alcune volte il programmatore scrive il codice di nuove funzioni, altre volte usa le funzioni create da qualcun altro. | ||
+ | |||
+ | Usando le eccezioni si possono dividere queste due attività: | ||
+ | * il programmatore che scrive il codice delle funzioni indica agli altri solo quale tipo di eccezioni vengono lanciate (e quando). | ||
+ | * il programmatore che usa una funzione scritta da qualcun altro indica solo cosa deve fare il suo programma quando riceve quel tipo di eccezione. | ||
+ | Ad esempio, nel caso in cui un programma non riesca a leggere il file specificato dall' | ||
+ | Lanciare eccezioni significa anche dover ripercorrere (srotolare) lo stack delle chiamate delle funzioni: un' | ||
+ | |||
+ | ===== Definizioni ===== | ||
+ | - Un blocco //try// racchiude il codice " | ||
+ | - Il blocco //try// deve contenere le condizioni (//if//) per distinguere la presenza di un problema o un errore. | ||
+ | - Uno o più blocchi // | ||
+ | - Ogni condizione (//if//) verificata dentro //try// deve " | ||
+ | - Dentro le parentesi tonde di ogni blocco //catch()// sarà indicato (come un parametro) il tipo di eccezione che quel blocco sa " | ||
+ | - Dopo il blocco //try// si trovano uno o più blocchi //catch()// che ricevono il controllo del flusso quando il tipo di parametro che viene loro " | ||
+ | - Durante l' | ||
+ | - Per la definizione precedente, quindi, per il passaggio del parametro, non si dovrebbe usare un puntatore ad una variabile locale di try, ma al massimo un puntatore a una variabile allocata con //new//, ricordando di disallocarla con //delete// dentro // | ||
+ | - Sempre per la stessa ragione, il 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... Meglio usare i //const reference// | ||
+ | |||
+ | {{: | ||
+ | |||
+ | ===== 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 (RAII) |