Newsgroup: it.comp.lang.c++
Titolo: [LONG] STL - Filosofia

Salve a tutti,
non sono un programmatore C++, nel senso conosco ( sicuramente meno di 
voi ) il linguaggio ma non scrivo molto codice in C++ sopratutto 
ultimamente.
 
Attualmente sto "traducendo" ( se così si puo' dire ) le STL in un altro 
linguaggio pero' non capisco la filosofia di alcune scelte. In 
particolare quella di deallocare un oggetto ( chiamando il distruttore ) 
una volta tolto dalla struttura dati ( comportamento trasfersale tra le 
code, liste ecc).
 
Potrei copiarle così come sono e fregarmene. O potrei fare a modo mio ma 
vorrei tanto capire il perché di tale scelta. E chiedo a voi di aiutarmi 
nella comprensione :)
 
Mettiamo per esempio che io abbia un Sistema Operativo scritto facendo 
pesante uso delle STL. Focaliziamoci sul Scheduler: ovviamente al suo 
interno troviamo la coda dei processi ( in realtà è una coda con 
priorità ma lasciamo stare ).
 
In un primo momento avrei pensato di organizare lo scheduler con due 
varibili, una la coda vera e propria e l'altra una varibile 
"ProcessoCorrente" con un codice del tipo
 
while ( true ) {
    CodaProcessi->Push( ProcessoCorrente );
    ProcessoCorrente = CodaProcessi->Pop();
    Esegui( ProcessoCorrente );
}
 
ma qui mi scontro con la filosofia delle STL. la Pop non solo non 
restituisce l'elemento in cima alla coda, ma addirittura lo distrugge.
 
stessa cosa con le liste quando si toglie un elemento da una lista ( 
magari per analizzarlo ) questo viene distrutto.
 
Per esempio mettiamo che io ho un programma diviso concettualmente in 3 
parti sequenziali. La prima legge un file e riempie una lista con delle 
informazioni. Nella seconda faccio uso delle informazioni contenute 
nella lista ( magari riccorendo a iteratori e simili ) e nella terza 
fase in modo sequenziale ( dal primo elemento all'ultimo ) utilizzo per 
l'ultima volta i dati contenuti nella lista. Io mi aspetterei qualcosa tipo
 
while ( !lista->empty() ) {
    temp = lista->pop_front( );
    // faccio qualcosa con temp
     ....
    // ed infine elimino temp
    delete  temp;
}
 
per le liste è più semplice perché posso semplicemnte fare
 
while ( !lista->empty() ) {
    temp = lista->front( );
    // faccio qualcosa con temp
    lista->pop_front();
}
 
Ma la domanda rimane: Dov'è che sbaglio nel mio raggionamento?

 Non so se mi sono spiegato in modo adeguato... se hai dubbi chiedi pure.
 
mi sa che mi odierarai :D

Il 09/01/2012 9.10, Dacav ha scritto:
 > On Mon, 09 Jan 2012 05:12:13 +0100
 > Penso che il "tuo" problema stia nel fatto che ragioni a puntatori.
 
E cosa ci sta nel male in questo? Anzi pensavo fosse un bene O__o
 
 
 > Se
 > nelle strutture dati di stl inserisci dei puntatori, tutto funziona come
 > lo desideri tu (es. estrai dalla lista il *puntatore* e quindi distruggi
 > il *puntatore* - i.e. il tuo oggetto rimane intatto).
 
Uh? Sicuro? Cioè è ovvio che sei sicuro altimenti non l'avresti detto ma 
mi lascia qualche perlessità se io ho un puntatore a una classe ( ad 
esmepio foo ) e chiamo la delete su di esso, la delete non dealloca la 
memoria occupata dalla classe chiamando il suo distruttore?
 
se io ho
 
myfoo * foo = new foo()
myfoo->do()
delete foo;
 
questo codice non chiama il distruttore foo::~foo() deallocando la 
memoria usata dalla classe?
 
Mi sfugge qualcosa...
 
 > Il C++ è pensato per astrarre questa parte, togliendo l'uso dei puntatori
 > e lavorando di references.
 
Cosa sono i references? ^o)?
 
 > Ovviamente questo implica che gli oggetti vengono deallocati dalla lista
 > quando fai L.pop(), ma precedentemente tu avrai fatto una copia fisica di
 > quest'oggetto:
 
Avevo pensato a questa cosa, ma questo computazionalmente non è un 
disastro? Ho un oggetto X lo clono creando l'oggetto Y e poi distruggo 
l'oggetto X... non mi sembra una cosa molto furba.
 
 > Per altro nessuno ti impedisce di lavorare direttamente con l'oggetto che
 
Sulla lista puo' anche andar bene, ma per la coda del mio messaggio 
orginale?
 
 > Non so se mi sono spiegato in modo adeguato... se hai dubbi chiedi pure.
 


 restituisce l'elemento in cima alla coda, ma addirittura lo distrugge.
 
 
 
Se ricordo bene (è un pò che non tocco C++)
gli algoritmi della STL sono sicuri verso le eccezioni.
La regola base è:
se viene sollevata una eccezione l' oggetto non deve avere subito 
modifiche per non rischiare dei memory leak
Questo è il  motivo per qui pop non può restituire T
 
Tempo fa (molto tempo fa) lessi un libro, exceptional c++ di Sutter,
dove, appunto viene spiegato questo
 
[CUT]
 

 l'oggetto X... non mi sembra una cosa molto furba.
 
non sembrerebbe che sia ciò che accada (ammesso poi che si 
cloni alcunché)
ciao
Soviet


 restituisce l'elemento in cima alla coda, ma addirittura lo distrugge.
 
Dove sta scritto che la "pop" deve restituire un elemento? La "pop" ha 
il solo scopo di rimuovere il primo elemento dello stack. Per accedere 
al primo elemento si usa la "top".
Anche lo stack, come tutti i contenitori, distrugge un elemento che 
viene tolto dal contenitore, e, se viene assegnato a un altro oggetto, 
costruisce tale oggetto; ma se tale oggetto aveva già un valore, viene 
prima distrutto tale oggetto. Memorizzando dei puntatori nel 
contenitore, non si hanno costruttori ne' distruttori chiamati, a meno 
di chiamare gli operatori "new" e "delete".
Quindi per scrivere del codice efficiente, la regola semplicistica e' 
"usa contenitori di oggetti, se il distruttore di tali oggetti non 
esiste (o non fa niente), altrimenti usa contenitori di puntatori".
 
La filosofia del C++ e' consentire la massima efficienza con ogni tipi 
di dato. Se il contenitore deve contenere oggetti la cui distruzione e' 
a costo zero, sarebbe inefficiente allocarli separatamente e memorizzare 
nella collezione il solo puntatori a tali oggetti. Se viceversa deve 
contenere oggetti la cui distruzione e' costosa, potrebbe essere 
inefficiente allocarli internamente alla struttura.
 
In realta' la questione si complica se si usa il nuovo standard C++11, 
che introduce alcuni template di classi di smart-pointer di gestione 
della memoria dinamica, e alcuni costrutti che implementano la 
move-semantics. Cose di cui sarebbe lungo parlare.
Comunque vari tipi di smart-pointer esistono come librerie non-standard 
(per esempio in Boost) e sono utilizzabili anche con i compilatori attuali.
 
-- 
 
Carlo Milanesi
http://carlomilanesi.wordpress.com/
 

 mi sa che mi odierarai :D
 
Naah. Ho dato testate nel C++ prima di capirlo. Il minimo che possa fare
(tempo permettendo) è aiutare chi sta facendo il mio stesso percorso a
capire certe cose.
 
Saluti!
 
[1] 
http://cplusplus.com/reference/std/memory/auto_ptr/
 


 Non ho capito la domanda.
 
Non importa. Mi sono dato da solo la risposta grazie alle tue spiegazioni :)


 
E grave che non ho neache capito cosa stai dicendo? Nel senso l'ho 
intuito... ma capire è un altra cosa...

 intuito... ma capire ï¿œ un altra cosa...
 
Era solo un accenno per avvertire che le cose non sono cosi' semplici 
come nella regoletta di prima.


 spiegazioni :)
 
Figurati :-)


 hanno niente a che fare con le funzioni matematiche.
 
Caro Carlo, ho una soluzione per tutto ciò...
 
HASKEELLLL! HASKEL per tutti!!! :-) LOL



 
Si impara sempre qualcosa di nuovo...

 stack pointer e retrocede lo stack pointer.
 
Potrebbe essere una spiegazione in effetti...