Cerca nel blog

Loading

4 febbraio 2008

Smart pointer: come evitare i memory leak

Vi ricordate l'ultimo post relativo alla programmazione? Vi avevo promesso una spiegazione piu' dettagliata su come evitare memory leaks ed eccomi qui a mantenere la promessa.

Possiamo definire il memory leak come un errore di programmazione in cui a seguito di una assegnazione dinamica di memoria nello heap non vi sia una corrispondente "liberazione" della stessa. Forse e' meglio fare qualche esempio:

void funzioneLeaky() {

   MyClass * miaClasse = new MyClass;

   // fai qualcosa e poi ritorna
   return;
}

void funzioneNonLeaky() {

   MyClass * miaClasse = new MyClass;

   // fai qualcosa ma prima di ritornare ricordati di "liberare"
   // mia classe
   delete miaClasse;

   // non e' necessario, ma utile
   miaClasse = 0x0;

   return;
}


La differenza tra le due implementazioni e' evidente, talmente chiara che potreste essere portati a credere che commettere un tale errore sia praticamente impossibile. Ma non e' proprio cosi'!

Ci sono, infatti, delle situazioni in cui anche se il codice e' scritto come nel caso della funzione non leaky, l'esecuzione del programma ha effettivamente degli sprechi di memoria. Ma come e' possibile? La spiegazione e' semplice, fintanto che l'implementazione del codice e' solo di poche righe allora non ci saranno problemi, ma quando le cose si fanno piu' complesse allora e' possibile che la funzione ritorni prima della chiamata a delete, oppure che una istruzioni lanci una exception (questa sara' l'argomento di un'altra lezione) e quindi termini inaspettatamente senza avere la possibilita' di liberare la memoria. Una possibile via d'uscita esiste e si basa sull'uso dei cosiddetti smart pointers, ovvero dei puntatori creati dinamicamente che vengono automaticamente distrutti non appena escono fuori di scope, come avviene per le variabili nello stack.

Tra tutte le implementazioni di smart pointers, quella piu' comunemente usata e' auto_ptr essendo disponibile nelle librerie C++ standard e denifinata nell'include memory. Ecco come il codice della nostra funzione sarebbe usando l'auto pointer:

void funzioneAutoPointer() {

   auto_ptr<MyClass> miaClasse( new MyClass );

   // fai tutto quello che devi. Puoi usare i metodi di miaClasse
   // come se fosse un normale puntatore, per esempio:
   miaClasse->FaiQualcosa();

   // ritorna e non ti preoccupare di liberare la memoria tanto
   // verra' automaticamente liberata.
   return;
}

Come funziona l'auto_ptr?

auto_ptr e' una classe template come avrete notato dall'uso delle parentesi angolari <>. Il costruttore dell'auto pointer prende come argomento un puntatore ad un oggetto della classe specificata nel template e lo assegna ad un membro data; inoltre viene ridefinito l'operatore "->" in modo che tutti i metodi della classe MyClass vengano applicati direttamente al membro data.
L'auto pointer viene generalmente costruito sullo stack, quindi il suo distruttore viene automaticamente chiamato non appena va fuori scope. Ovviamente l'implementazione del distruttore e' tale che viene distrutto il membro data associato all'auto pointer, liberando completamente la memoria occupata.
In altre parole, nella nostra funzione, qualunque cosa succeda dopo la creazione di miaClasse, la memoria ad essa associata verra' completamente liberata. Geniale vero?

Ma se non volessi cancellare la memoria?

Nell'esempio precedente, l'oggetto miaClasse veniva temporaneamente utilizzato solo all'interno della funzione, quindi e' doveroso distruggerlo al termine dell'esecuzione. Esisto pero' dei casi in cui un oggetto viene creato all'interno della funzione, ma lo vogliamo tenere vivo anche al termine della funzione e continuare ad utilizzarlo. Allora la domanda e': come possiamo usare auto_ptr in quel caso? Vediamo un esempio:

// funzione ritorna un puntatore a MyClass
MyClass * funzione() {

   auto_ptr<MyClass> miaClasse( new MyClass );

   // fai qualcosa con mia classe
   miaClasse->FaiQualocosa();

   // e poi ritorna il puntatore al nuovo oggetto
   // senza distruggerlo

   return miaClasse.release();
}


Nel codice qui sopra riportato, un auto pointer viene creato e gli viene assegnata un nuovo oggetto di tipo MyClass. La proprieta' di questo nuovo oggetto e quindi l'implicita responsabilita' di distruggerlo e' dell'auto pointer fino all'ultima riga dove viene chiamato il metodo release. Con questa chiamata, l'auto pointer viene liberato della proprieta' del nuovo oggetto e quando miaClasse verra' distrutto il nuovo oggetto continuera' ad esistere e (si spera) che verra' distrutto dal nuovo proprietario.
Notate come la chiamata a release avvenga con l'operatore "." e non "->". Possiamo interpretarlo nel modo seguente: per accedere ai metodi della classe auto_ptr (come release(), get(),...) si deve usare l'operatore ".", mentre per accedere a quelli dell'oggetto puntato si usa l'operatore "->" come appare naturale per un puntatore.

Ma l'auto_ptr e' o non e' un puntatore?

La risposta e' chiaramente no, anche se contiene il puntatore all'oggetto che abbiamo creato. Questa sottigliezza e' alla base di parecchi errori di compilazione quando una funzione richiede come argomento un puntatore e il programmatore gli passa un auto_ptr. Vediamo un esempio anche per questo caso:

void altraFunzione(MyClass * argomento) {

   argomento->FaiQualcosa();

}

void funzione() {

   auto_ptr<MyClass> miaClasse(new MyClass );

   // chiama altraFunzione, passando il puntatore non l'auto pointer!
   altraFunzione( miaClasse.get() );

return ;

}

L'auto pointer non e' un puntatore! Se vogliamo usare il puntatore all'oggetto associato all'auto pointer dobbiamo usare il metodo get(), attraverso l'operatore ".". Attenzione che il metodo get() lascia la proprieta' del puntatore all'auto pointer e quindi la memoria verra' liberata quando l'auto pointer viene distrutto.

Ma allora perche' non usare sempre gli auto pointer?

Bella domanda! Infatti dovremmo sempre utilizzare gli auto pointer, ma ci sono delle importanti limitazioni da tenere in considerazione, in particolare per quanto riguarda la creazione di copie. Il messaggio generale e' che per gli auto pointer le copie non sono equivalenti! Per chi non se fosse reso conto, questo ha delle pesate implicazioni quando gli auto pointers sono usati all'interno di contenitori come i vettori, le mappe eccetera.

Per esempio il codice seguente, a seconda del compilatore usato, potrebbe compilare o no, ma comunque il suo esito e' decisamente incerto

// esempio da NON USARE!
void sortVector() {

   vector<auto_ptr<MyClass>> vettore;
   sort( vettore.begin(), vettore.end() );

}


Come possiamo venirne fuori?

Ci sono altre implementazioni di smart pointers... ma questa e' un'altra storia...

Installare Fedora da Windows

Il titolo forse e' un po' esagerato, ancora non e' completamente vero, ma ci stiamo avvicinando. Per Ubuntu, esiste gia' un semplice applicativo Windows che, una volta lanciato, ti permette di installare Linux su una partizione libera o da formattare. Si tratta di una bella semplificazione soprattutto nel caso di utenti Windows che vogliono provare l'ebrezza di vedere cosa si nasconde dietro al pinguino.

Per Fedora (la mia distribuzione preferita) questo tool ancora non esiste, e se uno vuole installarla ha solo due possibilita':
  • seguire la via classica: cioe' scaricarsi un'immagine ISO con l'installazione da uno dei numerosi mirror, masterizzarlo su un CD e quindi riavviare il PC dal CD.
  • oppure passare attraverso una distribuzione live: che significa, scaricarsi l'immagine del disco, masterizzarla, eseguirla ed eventualmente installarla in modo permanente sul disco fisso.
Ora, pero', c'e' un'opzione in piu'. Vi spiego cos'e' e come funziona. Dal proprio PC con Windows in esecuzione, l'utente puo' scaricarsi l'immagine di una Fedora life e un piccolo applicativo Win32. Una volta lanciato, questo programma cerchera' tutti i dischi rimovibili connessi al sistema e, su quello prescelto dall'utente, installera' il contenuto dell'immagine ISO senza necessita' di masterizzarla e di riavviare il PC.

Come vedete il passo da qui all'installazione completa su disco rigido da Windows e' molto breve. Speriamo che avvenga presto, in modo che sempre piu' gente si converta ad un sistema operativo serio!

Volete saperne di piu'? Leggete qui.

3 febbraio 2008

La Lombardia fa il pieno in Svizzera

(Clicca qui per un aggiornamento sui prezzi)

E' ormai da parecchi anni che la regione Lombardia ha instaurato una politica di riduzione prezzi della benzina nel tentativo di limitare il flusso di automobilisti che quotidianamente si recano nella vicina Confederazione Elvetica per riempire il serbatoio. Il meccanismo è semplice: ad ogni autovettura a benzina immatricolata nella provincia di Como e Varese viene associata una tessera elettronica (tipo carta di credito) che permette di ottenere un sconto sul prezzo esposto della benzina.

L'entità dello sconto dipende dalla distanza rispetto al confine Svizzero del luogo di residenza del titolare. Nel mio caso specifico, abitando a pochi chilometri della frontiera, ho diritto al massimo sconto equivalente a 0.179 € al litro.

Ma chi ce li rimette questi soldi? La Regione. Infatti parte della tassazione sui carburanti viene deviata alle Regioni.

Ma perché? Per guadagnarci di più tutti. Ecco come: la regione rinuncia a parte della tassazione, ma favorisce i distributori italiani lungo il confine che quindi aumentano il loro giro d'affari (e quindi le tasse). In più il mancato guadagno della regione viene largamente ricompensato dall'aumentata quantità di prodotto venduto sul suolo patrio. E anche gli automobilisti sono contenti perché pagano la benzina ad un prezzo comparabile a quello svizzero senza dover espatriare per fare il pieno.

Quello che sembrava un tipico esempio di circolo virtuoso si è però spezzato a causa dei incrementi dei prezzi dei carburanti senza un corrispondente adeguamento dello sconto. E così, questa sera sono tornato in Svizzera a mettere benzina.

Volete le cifre?
Eccovi serviti:

  • Costo benzina al litro in Italia: € 1.337 (questa sera al distributore Q8)
  • Sconto Fascia "A" al litro: € 0.179
  • Costo netto in Lombardia al litro: € 1.158
  • Costo benzina al litro in Svizzera: € 1.079 (1.740 CHF con tasso di cambio come da xe)

I numeri parlano da soli: un risparmio di 79 millesimi di Euro per ogni litro di carburante, equivalenti a più di 3 Euro su un pieno medio di 40 litri. Anche ammettendo di fare una deviazione di 10 km per recarsi oltre confine e di sprecare un litro di benzina per questo si risparmiano comunque dei soldini.

Morale della favola: io torno in Svizzera a fare il pieno!

2 febbraio 2008

Il diagramma a busta

Settimana scorsa ero ad Amburgo per un meeting di collaborazione e durante un momento di pausa ho avuto la fortuna di partecipare ad una chiacchierata tra colleghi riguardo la vita degli scienziati. In questi ultimi tempi, infatti i ricercatori si devono dare alla ricerca dei fondi se vogliono continuare ad esercitare la loro professione.

Credo che il grafico qui sotto, che chiamerei diagramma a busta, sia una buona rappresentazione di come il tempo di uno scienziato venga suddiviso in funzione della sua anzianità professionale.


Cerco di spiegarvelo un po' più nel dettaglio. La vita professionale di un giovane ricercato inizia più o meno a 25 anni con il suo dottorato, in quella fase, ovviamente, il suo tempo sarà totalmente dedicato alla ricerca senza nemmeno rendersi conto che esiste qualcosa al di fuori di essa. Poi si diventa grandi, e per arrotondare lo stipendio, si inizia a dedicare un po' di tempo all'insegnamento e contemporaneamente ci si accorge che i fondi per la ricerca sono troppo poveri e serve guardarsi in giro in cerca di qualche finanziamento. Tutto questo va ovviamente a scapito del tempo dedicato alla ricerca!

La situazione raggiunge l'apice a metà della vita professionale (a 45 anni nel nostro esempio), quando la ricerca è solo un piacevole lontano ricordo e il tempo lavorativo è equamente diviso tra attività didattiche, impegni burocratici e un'estenuante ricerca di finanziamenti, indispensabile se si vuole tenere insieme quel gruppo di persone più giovani che ancora riescono a fare ricerca.

Da qui in avanti è tutto in discesa, si fa per dire! Con l'avvicinarsi della pensione, oops! volevo dire dell'età della saggezza, si viene sollevati da alcuni compiti e, se si è stati dei bravi scienziati, si viene invitati a ripetizione a congressi e conferenze.

Cari miei giovani scienziati, è chiaro che la prospettiva non è troppo allettante, ma cerchiamo di tenere duro fin tanto che si può!

P.s. Se volete vedere il codice che ho usato per generare il diagramma a busta, lo trovate qui!

A tutti i complottisti!

Vi è mai capitato di incontrare un complottista? Sì una di quelle persone che vedono complotti celati dietro tutti gli avvenimenti della vita quotidiana. A loro è dedicata la vignetta di ieri di Dilbert.

Eppure ce ne sono, io ne conosco alcuni e sono anche molto simpatici! Loro lo sanno che ogni tanto faccio fatica a credere a tutto quanto vedono nascondersi dietro gli avvenimenti comuni, anche se, devo ammettere, ogni tanto ci azzeccano! Quando questo succede, è veramente l'apoteosi!

Visto, lo sapevo che andava a finire così. Te lo avevo già preannunciato quando ancora nessuno ne parlava in giro.
E nessuno mi crede e vedrai quando anche le altre cose che ti ripeto sempre si avvereranno...
Gli alieni per esempio....


A tutti loro, voglio assicurare che dietro questo post non c'è proprio niente. Anzi no qualcosa c'è: un sorriso e un affettuoso ricordo per tutti voi!

La grande paura

Ieri, venerdì, avevo il pomeriggio libero! Avevo già programmato tutto: finire di leggere Harry Potter e scrivere il post a riguardo poi giocare un po' con ROOT perché volevo disegnare uno di quei plot con bande di confidenza che da sempre mi attirano molto e invece....

Invece entro in casa alle 13.30 e noto con disappunto che le lucine sul mio modem ADSL sono sì accese, ma nella sequenza sbagliata: quella che indica la mancata connessione.

Chiaramente ho cominciato con le procedure standard, cioè spegni e riaccendi il modem, verifica i collegamenti, ma tutto senza fortuna. Disperato, stavo già per chiamare il call center di Tiscali, quando alzando la cornetta del telefono, al posto del consueto "Tu-Tu" di benvenuto, sentivo soltanto un fastidioso fruscio.

Ecco spiegato l'arcano, non era un problema di ADSL, ma un vero e proprio problema di linea telefonica. Anziché Tiscali, ho chiamato il 187 di TelecomItalia per segnalare il guasto. L'operatore, dopo le domande di rito, mi assicura che in 48 lavorative (?!? non si capisce se sono equivalenti a 2 o a 6 giorni lavorativi) mi avrebbero sistemano il problema in centrale. Nella migliore dell'ipotesi questo equivaleva a non avere il telefono, ma soprattutto internet, per tutta la durata del week end!

Sicuramente capirete il mio stato d'animo, non solo i miei piani per il pomeriggio erano stati completamente mandati all'aria, ma quelli del week end rischiavano una sorte simile. Per fortuna, questa mattina (sabato) mi sono alzato e ho trovato con grande sorpresa il telefono che squillava e la connessione perfettamente funzionante. Per questa volta il pericolo è stato scampato!

Harry Potter e i doni della morte / 2

Titolo originale: Harry Potter and the Deathly Hallows
Titolo italiano: Harry Potter e i doni della morte
Autore: Rowling, J. K.
Ambientazione:
Mondo magico nei pressi di Londra
Giudizio:
Più che buono

Commento: Settecento pagine di vero thriller. Harry Potter è cresciuto e i suoi lettori con lui, quindi non aspettatevi un libro per bambini. La trama è veramente molto avvicente e scorre veloce senza mai perdere il ritmo.


(ATTENZIONE: nel seguito viene rivelata parte della trama)

Il romanzo inizia con Harry a casa dei suoi zii Babbani per trascorrere le vacanze estive mentre tutto il mondo magico è in subbuglio a causa del ritorno ufficiale del signore oscuro al potere. Il protagonista sa che quest'anno non ritornerà a Hogwarts (la scuola per maghi e streghe) per mantenere la promessa fatta al defunto Prof. Albus Silente di trovare e distruggere gli Horcrux, ovvero oggetti maledetti in cui Colui-Che-Non-Deve-Essere-Nominato ha deposto un frammento della sua anima. In questa missione non sarà solo, seguendo il consiglio dell'ex preside si farà accompagnare solo dai due fidati amici Ron e Hermione, che per chi se lo fosse scordato si sono innamorati. Il mondo intanto è allo sbando, il ministro della Magia è solo un fantoccio messo lì dal Voldermort, e il ministero pullula di Mangiamorte. Anche ad Hogwarts la situazione non è migliore, Severus Piton è il nuovo preside ed un regime di assoluto rigore viene stabilito.

Harry diventa maggiorenne (a 17 anni nel mondo magico) e quindi perde la traccia, questa è un particolare incantesimo imposto su tutti gli appartenenti al mondo magico dall'età di 11 anni fino a 17, in modo da essere immediatamente rintracciabili qualora usassero la magia al di fuori delle mura della scuola. Per Harry questo assume anche un altro significato: diventare maggiorenne e lasciare per sempre la casa degli zii Babbani equivale a perdere la protezione d'amore che sua madre Lilly ha deposto su di lui il giorno della sua morte. In altre parole, ora Tu-Sai-Chi potrà attaccarlo e batterlo in un duello magico.

La ricerca degli Horcrux, inizia e i tre si ritrovano in una situazione molto spiacevole, non solo non sanno dove cercarli, ma non sanno nemmeno cosa devono cercare di preciso. E poi, viene il problema di come fare a distruggerli. Manco a dirlo, in tutto questo vengono utili i doni, alquanto bizzarri, che Silente ha lasciato loro nel suo testamento. Vi starete domandando: sono questi i doni della morte (vedi titolo)? Certo che no! Questi sono un'altra cosa, direi un'altra tentazione a cui i ragazzi devono resistere per evitare di commettere il medesimo errore del loro beniamino Silente e dello stesso Voldemort.

In tanti si sono chiesti, se Harry Potter morirà al termine del libro, e in effetti per un capitolo buono sembra che questo debba essere l'epilogo, ma poi all'ultimo momento le cose cambiano e ancora una volta l'amore del Giovane-Che-è-Sopravvissuto batte l'odio e la fame di potere del signore oscuro. L'epilogo, invece, è una bella fotografia di famiglia scattata 19 anni dopo. Harry e Ginny (la sorella di Ron) si sono sposati e hanno avuto tre pargoletti, lo stesso dicasi per Ron e Hermione e le due famiglie si ritrovano alla stazione dove i loro figli più grandi stanno partendo per la scuola. E' bello il dialogo a due tra Harry e il suo secondogenito, Albus Severus: il piccolo è spaventato di poter finire nella casata dei Serpeverde, mentre desidererebbe appartenere a quella dei Grifondoro come mamma e papà. A questa preoccupazione, Harry risponde che non ha nulla da temere, visto che lui porta il nome dei due più grandi presidi della scuola (Silente e Piton), un Grifondoro molto intelligente e un Serpeverde dal coraggio smisurato. Sì sì avete capito bene, stiamo proprio parlando di Piton, ma questo è un mistero che non vi voglio svelare...

Il libro si conclude con queste battute tra Ginny e Harry riferite al figlio appena partito per la scuola:

"Non avrà problemi" mormorò Ginny.
Harry la guardò e distrattamente abbassò la mano a sfiorare la cicatrice a forma di saetta sulla fronte.
"Lo so"
La cicatrice non gli faceva male da diciannove anni. Andava tutto bene.


E che la saga sia veramente finita?