Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
...e siccome ritengo che leggere da una fonte diversa le cose che già si sanno permette di scoprire particolari persi, fraintendimenti, pensieri nascosti tra le righe e dubbi ai quali manco si era pensato sto scorrendone le pagine. Sono dunque giunto alla sezione dove si tratta la gesione degli errori e, inevitabilmente, viene tirato in ballo il meccanismo throw/catch. In particolare, c'è una frase che attiva nel mio cervellino il timore d'aver seguito fino ad ora una strada sbagliata in merito. Ecco la frase:
"In termini tecnici, la funzione che rileva l'errore 'solleva' o 'lancia' (throw) un'eccezione ('marcandola' in qualche modo, come vedremo) e termina: l'area stack è ripercorsa all'indietro e cancellata (stack unwinding) a vari livelli finchè il flusso del programma non raggiunge il punto (se esiste) in cui l'eccezione può essere riconosciuta e 'catturata' (catch)"
La perplessità nasce dal quel "l'area dello stack è ripercorsa all'indietro e cancellata"... perché "e cancellata"? Significa FORSE (non ci avevo mai pensato) che se in precedenza ho allocato della memoria dinamica il meccanismo provvede IN AUTOMATICO a liberarla senza alcun intervento da parte mia? Ovvero, volendo fare un esempio...
In questo programma senza senso, sul mio PC con due soli gigabyte di RAM, alla creazione dell'oggetto "pippo" il costruttore alloca dinamicamente senza problemi s1[64], ma ha qualche "problemino" ad allocare s2[0xFFFFFFFF]. Quando l'allocazione fallisce, new lancia un'eccezione bloccando l'esecuzione del costruttore, ma ormai un po' di memoria dinamica è già stata allocata e dovrebbe essere liberata. L'esecuzione viene quindi "rimandata" al blocco catch del costruttore dove il messaggio d'errore standard viene convertito in PIPPOErr_AllocError e "rimbalzato" nel main per l'ulteriore eventuale elaborazione.
Fino ad oggi, per evitare memory leaks, il mio modo di procedere è sempre consistito nel provvedere a un costruttore che liberasse "attivamente" la memoria già allocata prima di "rimbalzare" l'errore, tipo...
Codice sorgente - presumibilmente C++
PIPPO::PIPPO(unsignedlong dim ){
try{
s1 = s2 =NULL;
s1 =newchar[64];
s2 =newchar[dim];
nChars = dim;
}catch( ... ){
delete[] s1;
throw PIPPOErr_AllocError;
}
}
... ma la frase che ho riportato, come ho detto, pare lasciar intendere che la memoria allocata venga liberata in automatico (benché gli "esperimenti" che ho fatto sembrino dimostrare il contrario).
Se c'è una cosa che detesto è non avere le idee ben chiare su questo genere di cose, perché poi pianto su dei pasticci senza né capo né coda e mi demotivo di brutto. Chi mi aiuta?
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
Quella frase si riferisce al call stack. E new prima di allocare controlla che ci sia lo spazio, perché new alloca un solo blocco contiguo di memoria, quindi non lascia pezzi allocati di memoria: o alloca tutto o niente. Puoi vedere un throw come una forzatura a continui return fino al ritrovamento del catch. Capito?
Sì, ho capito la questione dei return "a catena", ma nella situazione che ho esemplificato, new è riuscito ad allocare con successo la porzione di memoria richiesta con la prima chiamata, fallendo nell'allocare la seconda. Dunque, quando "scatta" il return forzato, il primo pezzetto di memoria allocato ha due possibili destini: o rimane lì (ed è quello che ho pensato finora) e va quindi deallocato "manualmente" con delete, o viene deallocato automaticamente da qualche meccanismo intrinseco. Forse la risposta è nascosta nel fatto che le funzioni vengono allocate nello stack mentre new alloca da un'altra parte (nell'heap?)), per cui "cancellare lo stack" non significherebbe liberare la memoria allocata dinamicamente da new, ma solo quella allocata per fare spazio alle funzioni e alle loro variabili e parametri non dinamici. Ci ho preso?
Scusa se parlo come mangio, ma sono un autodidatta e questo ha le sue conseguenze.
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
New è diverso da malloc per due ragioni : non è detto che new allochi nell heap E new chiama il costruttore. Scusa se ho frainteso la domanda, la risposta è : la prima memoria allocata rimane allocata. Se non liberi la memoria rimane allocata. Non so se alla terminazione del programma la runtime del so elimini la memoria, ma questo non è un comportamento standard. Quindi a lanciare eccezioni tra new (riuscito) e delete si incorre in memory leak.
TheDarkJuster: "[...] la prima memoria allocata rimane allocata
Allora era giusto il metodo che seguivo prima: intercettare l'eccezione con catch nell'ambito del costruttore e "far fuori" la memoria già allocata prima di "rimbalzare" l'eccezione con un nuovo throw. Continuerò a far così. Peccato perché sarebbe stato molto più lineare nel caso opposto... forse è stato il treno dei desideri a indurmi a voler credere che la frase di quel manuale sottintendesse un qualche automatismo magico!
Grazie per avermi aiutato a sciogliere il dubbio.
Ah, per quel che ne so (e non è certo una garanzia!) in Windows quando un processo viene terminato tutto quello che c'è di dinamico che compete a quel processo viene distrutto senza pietà -- file aperti, finestre aperte, oggetti GDI, memoria allocata... tutto. Sai cosa, pensavo che potrei scrivere un sistema operativo che gestisse in automatico ancora più di quello che fa Windows, cosa ne dici? Vuoi partecipare?
ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
Quando viene scatenata un'eccezione, lo stack viene "cancellato", ma gli spazi allocati con new/malloc devono essere manualmente liberati (con delete/free).
Ultima modifica effettuata da pierotofy il 23/08/2015 alle 19:29