Newsgroup: it.comp.lang.c++
Titolo: list di STL e distruttori

Ciao, questo e' il mio primo approccio alla STL e ai template in generale.
Sto usando C++ Builder 1.
Mettiamo che costruisco una classe MyClass che ha al suo interno un
puntatore allocato, per cui il distruttore comprende anche il delete per
quel puntatore.
poi dichiaro una lista di puntatori con
list<MyClass *> mylist;
e poi faccio i miei vari
MyClass *p = new Myclass();
mylist.insert(mylist.begin(), p);
Se poi faccio
mylist.remove(mylist.begin());
viene chiamato il distruttore di MyClass ???
Da una prova che ho fatto pare di no, ma e' giusto? Cioe' ... c'e' modo di
farglielo fare? E' scomodo doverlo fare a mano....
nel file "memory" incluso dal file "list" trovo:
template <class T>
inline void destroy (T* pointer)
{
#if defined(RWSTD_NO_DESTROY_NONBUILTIN)
    delete (__FS<T>*) (pointer);
#else
    pointer->~T();
#endif
}
da cui pare che la cosa dipenda da quella costante.
Qualcuno mi spiega un po' come funziona la cosa?
Grazie!

 Qualcuno mi spiega un po' come funziona la cosa?
Quella costante è una caratteristica del tuo compilatore (che comunque è
obsoleto). Se vuoi il secondo dei due comportamenti prova a scrivere
#undef RWSTD_NO_DESTROY_NONBUILTIN
prima dell'#include.
EMG

 viene chiamato il distruttore di MyClass ???
No.
Viene chiamato il distruttore degli elementi della lista, ma in
questo caso gli elementi sono puntatori, che non hanno distruttore,
e quindi non succede niente.
	Mauro

 no?
Con delete distruggi l'oggetto puntato, non il puntatore.
La lista non chiama delete, e non potrebbe farlo, visto che
non sa se i soui membri sono puntatori.
Se hai:
  void func ()
  {
    MyClass a;
    MyClass * p = new MyClass();
    ...
  }
all'uscita della funzione viene chiamato il distruttore di  a
ma non quello di  *p  (verrebbe chiamato quello di  p  se ce
l'avesse).
La stessa cosa succede quando togli un elemento da una lista o
da un altro contenitore standard: l'elemento viene distrutto,
ma eventuali elementi puntati no.
	Mauro

non sa se i soui membri sono puntatori.
hm..  giusto. Quindi dovrei inserire nella lista l'oggetto e non il
puntatore.
Pero' quando faccio l'inserimento l'oggetto viene copiato nella lista vero?
Quindi se faccio
func()
{
  MyClass a;
  mylist.insert(mylist.begin(), a);
}
viene distrutto a, ma nella lista c'e' una copia giusto?
E quando poi rimuovo l'elemento dalla lista il distruttore dell'oggetto
viene chiamato.
Rispetto a fare una lista di puntatori gli svantaggi che vedo sono in
efficienza, perche'
1) ad ogni inserimento va fatta la copia dell'intero oggetto
2) per ogni operazione di manutenzione lavoro con oggetti invece che con
puntatori di soli 4 byte
C'e' qualche altro vantaggio/svantaggio?
C'e' un approccio "comune" che si segue in questi casi?
Grazie ciao
Roberto


Dunque. Intanto hai ragione tu: il distruttore della classe non viene
chiamato implicitamente. Poi c'è un altro problema: la sintassi proposta
per il metodo remove() a me non risulta valida. Mi risultano esistere
invece:
mylist.erase(mylist.begin());
oppure
mylist.remove(p);
In entrambi i casi è necessario distruggere esplicitamente il puntatore
con
delete p;
EMG

e quindi non succede niente.
Ma se io faccio
MyClass *p = new MyClass();
delete p;
il distruttore viene chiamato. E allora perche' se lo rimuovo dalla lista
no?
Ciao
Roberto

 EMG
Non vorrei sbagliarmi ma nel contenitore ci sono oggetti tipo T (nel
nostro caso T = MyClass *) e quindi si avrebbe che pointer e' di tipo
MyClass** (puntatore a puntatore a MyClass) e chiamarne il distruttore
dell'oggetto punato (che sarebbe un MyClass *) non chiama assolutamente
il distruttore della classe MyClass.
Andrea Polci
--
Andrea Polci    
apo...@geocities.com

mylist.remove(p);
Si, hai ragione mi sono sbagliato. intendevo proprio
mylist.erase(mylist.begin())

 C'e' un approccio "comune" che si segue in questi casi?
Potresti definire una lista di puntatori:
  template <typename T>
  class PtrList {
    std::list<T*> _list;
  public:
    ...
  };
Forse ci sono soluzioni migliori, pero' adesso non me ne vengono in mente.
	Mauro

e copt on write.
immagino che fosse "copy on write", ma devo ammettere che ancora non ho
avuto il piacere di fare la conoscenza ne' di questo concetto ne' del
reference counting .... :)
Grazie per tutto il resto.
Ciao
Roberto

 reference counting .... :)
Il concetto di "copy on write" e' abbastanza semplice.
Invece che copiare i dati di un oggetto si inserisce nel nuovo oggetto
un riferimento (non intendo un reference, non sto parlando
necessariamente di C++) agli stessi dati dell'oggetto originale.
In questo modo l'operazione di copia e' molto veloce.
La copia vera e propria si rende necessaria solamente quando si ha
bisogno di modificare i dati condivisi (sia partendo dal nuovo che
dal vecchio oggetto): da qui il nome di copy on write.
Da questa strategia nasce l'esigenza di un reference counting in quanto
si ha la necessità di sapere se i dati sono condivisi. In tal caso
occorre fare una copia prima di modificarli, oppure non bisogna
disallocarli.
Provo a fare un esempio in modo da essere piu' chiaro.
class Dati{
public: // tutto public per accorciare l'esempio, altrimenti occorre
	// definire una seri di funzioni
   int x; // I dati veri e propri
   unsigned int ref_count;
   Dati(){ ref_count = 1; }
   Dati(cont Dati &d) { x = d.x; ref_count = 1; }
}
class Pippo{
private:
   Dati *dati; // sono i dati veri e propri dell'oggetto
   int check_for_write();  // viene chiamata prima di qualsiasi
                           // modifica ai dati
public:
   Pippo();
   Pippo(const Pippo &p);
   ~Pippo();
   void Modifica();
}
int Pippo::check_for_write()
{
   if dati->ref_count == 1
      return 0;		// I dati non sono condivisi
   // Dati codivisi, crea una nuova copia
   Dati *tmp = new Dati(dati);
   dati->ref_count--;	// Decrementa il numero di riferimenti ai
			// dati condivisi
   dati = tmp;
   return 1;
}
Pippo::Pippo()
{
   dati = new Dati();
   ...
}
Pippo::Pippo(const Pippo &p)
{
   dati = p.dati
   dati->ref_count++;
}
Pippo::~Pippo()
{
   ...
   if dati->ref_count == 1
       delete dati;     // Dati non condivisi, distruggerli
   else
      dati->ref_count--;	// Dati condivisi, decrementare il
                                // reference count
}
void Modifica()
{
   check_for_write()
   ... // operazioni che modificano i dati.
}
Questa come implementazione non e' sicuramente granche', ma penso
che sia sufficiente a capire il concetto.
Il maggior problema sta nel mantenere aggiornato il contatore dei
riferimenti.
