mercoledì 9 settembre 2015

Puntatori intelligenti per problemi stupidi

Negli ultimi mesi ho studiato una parte delle novità introdotte negli ultimi anni nella libreria standard di C++.

I puntatori puri, le famose stelline * tanto famose con il C sono di fatto quasi deprecati, alcuni autori dicono che non ci sono ragioni oggi per usarli se non quando si presentino problemi di prestazioni o in caso di risorse molto ridotte.

Devo dire che districarmi tra puntatori, riferimenti, shared_ptr e unique_ptr per me abituato a Java, è stato piuttosto difficile: la gestione della memoria "semi automatica" del nuovo C++ e la scelta tra tenere un'istanza nello stack o nello heap impongono quasi di imparare a programmare di nuovo.

Attualmente il mio livello di comprensione mi porta alle seguenti conclusioni.

Effettivamente il puntatore puro è evitabile in un normale programma gestionale, a meno che non ci si debba interfacciare con delle librerie che lo richiedono.


In C++ oggi è importante stabilire quale oggetto è proprietario di un altro e il proprietario è quello che determina il rilascio del secondo oggetto, cioé quando il proprietario è distrutto, questo determina la distruzione del secondo oggetto. Tale operazione si può fare in automatico, usando un unique_ptr.

Nel caso di variabili di istanza, io ho individuato i seguenti casi:

Il riferimento indicato con la e commerciale & è comodissimo per fare riferimento all'istanza di un altro oggetto se sicuramente mai nullo e mai usato dopo il suo rilascio, senza che il primo ne sia il proprietario.

Una variabile di istanza di un oggetto di proprietà mai nullo, se non è troppo grande, può stare nello stack senza puntatori di alcun genere o riferimenti.

Un riferimento ad un oggetto di proprietà, ma che può valere null è un buon candidato per essere puntato da un unique_ptr o da uno shared_ptr, perché il riferimento & non può mai valere null.

Una variabile di istanza di un oggetto che è di proprietà di un altro oggetto, che potrebbe valere null, potrebbe essere un candidato per uno shared_ptr.

Una lista concatenata o un vettore di oggetti che sono proprietà di altri oggetti, dovrebbe contenere degli std::reference_wrapper.

Una lista concatenata o un vettore (si chiamano contenitori in C++, sarebbero le Collections di Java) di oggetti di proprietà,  può mantenere tali oggetti nello stack.

L'uso di shared_obj non poi così limitato come si legge in giro. Si usa dove si potrebbe usare & ma c'è il rischio che l'oggetto puntato sia tolto dalla memoria prima del riferimento e dove si cerchi di chiamare i metodi dell'oggetto tolto.

Questo unito all'impossibilità di valere null, è un vero e proprio rischio nell'usare il riferimento &. A differenza di Java, in C++ è possibile per errore togliere dalla memoria un oggetto che ha dei riferimenti attivi, così come è possibile tentare di cancellare gli oggetti più di una volta, creando problemi com'è ovvio...

In generale, a meno che non si tratti di oggetti enormi, conviene tenere tutto nello stack, perché è più semplice, il problmema è se il dato può valere null, personalmente non capisco perché si sia rimandata l'introduzione della classe Optional allo standard C++ 17, ma pazienza...

Dopo questi mesi di studio, mi sento di affermare che il C++ offre diverse possibilità di gestione della memoria manuale, che però richiede molta attenzione.


Nessun commento:

Posta un commento