Newsgroup: it.comp.lang.c++
Titolo: Le funzioni virtuali : trappole e trabocchetti nascosti

Ciao a tutti ,
tempo fa' stavo scrivendo del codice che faceva uso di una classe con
funzioni virtuali .
L'istanza di questa classe veniva salvata in un file quando alcuni
dei sui dati membri venivano modificati .
Successivamente l'istanza veniva recuperata dal file e ....
cominciavano i guai .
Capitava infatti che alcune volte il programma andasse in crash totale
quando cercavo di utilizzare una delle funzioni virtuali .
Questo e' il codice incriminato (molto semplificato) :
 class  BASE  {
        public :
        virtual void Fun(int) = 0 ;
       } ;
 class DER : public BASE  {
       public :
      int Val1 ;
      void  Funz(int) ;
     } ;
 void DER::Funz(int num)
{
 Val1 += num ;
}
// Questa e' la funzione che scrive l'istanza nel file (uso la
//libreria del C (Borland))
 int WOnFile(char *nomefile,BASE *ptr)
{
 FILE *stream = fopen(nomefile,"wb") ;
 if  ( ! stream ) return 0 ;
 fwrite(ptr,sizeof(DER),1,stream) ;		
 fclose(stream) ;
 return 1 ;
}
// Questa e' la funzione che legge l'istanza dal file 
 int RFromFile(char *nomefile,BASE *ptr)
{
 FILE *stream = fopen(nomefile,"rb") ;
 if  ( ! stream ) return 0 ;
 fread(ptr,sizeof(DER),1,stream) ;		
 fclose(stream) ;
 return 1 ;
}
 BASE *Master ;
 DER Der ;
 main()
{
 RFromFile("prova",&Der) ;
 Master = &Der  ;
 
// qui il programma .......
 Master->Fun(10) ; // possibile crash
// qui il programma .......
 WOnFile("prova",Master) ;
 return 0 ;
}
 Come si capisce dal codice (spero) succedeva che durante l'esecuzione
del programma alcune volte il sistema andava in crash proprio quando
si tentava di richiamare la funzione virtuale .
Solo dopo molti tentativi e numerose ore perse con il debugger ,ho
scoperto ,esaminando il codice in assembler ,che l'oggetto istanziato
ha una word aggiuntiva (nascosta all'inizio dell'istanza)  che viene
caricata nei registri un attimo prima di invocare la funzione
virtuale.
Si tratta forse del misterioso puntatore alla VTABLE contenuto in ogni
istanza di classe con funzioni virtuali ?
Solo in questo caso si spiegherebbe l'errore.
Infatti quando io salvo l'oggetto nel file vi scrivo anche il
puntatore implicito che pero' quasi sicuramente non sara' piu'
corretto quando lo rileggo dal file (il breve programma presentato non
lo dimostra ,ma gli oggetti vengono allocati e deallocati durante
l'esecuzione ) .
Questa e' la soluzione che ho adottato ma mi sembra un po' macchinosa:
 BASE *Master ;
 DER Der ;
 DER temp ;
 main()
{
 RFromFile("prova",temp) ;
//  copio i dati membro ma non il puntatore nascosto
 Der = temp ;  
 Master = &Der  ;
 
// qui il programma .......
 Master->Fun(10) ; // OK
// qui il programma .......
 WOnFile("prova",Master) ;
 return 0 ;
}
 
 Aspetto suggerimenti .....   e una Vostra conferma .
 grazie 
~~~~~~~~~~~~~~~~~
MAINARDI Gianfranco
Burago di Molgora (MI)
Italia
Email : 
main...@rete039.it
ICQ# number : 31334916
~~~~~~~~~~~~~~~~~

 cominciavano i guai .
Ci cascano tutti (io compreso!). Il problema e' che i metodi virtuali sono
virtuali soltanto quanto l'oggetto e' stato costruito.
Durante l'esecuzione del costruttore, i metodi virtuali sono chiamati in
maniera NON virtuale. Se nella tua classe c'e' un
virtual void QQQ()=0;
ti becchi una bella eccezione quando lo chiami nel costruttore!
La ragione di questa non virtualita' della chiamata sta nel fatto che,
quando gira il tuo costruttore, eventuali costruttori di classi da te
derivate non hanno ancora girato. Eventuali ulteriori riferimenti non sono
stati inizializzati, in poche parole la tua classe e' veramente Work in
progress!!
A propoposito: se throw-i un'eccezione durante un costruttore, vedrai tante
cose molto affascinanti...
Morale: costruisci l'oggetto e poi chiama il metodo virtuale. Se ti rompe
fare cosi', usa il Pattern Factory per incapsulare la costruzione.
Ciao!!!!
Alessandro Coppo
a.c...@iol.it
www.geocities.com/alexcoppo