Newsgroup: it.comp.lang.c++
Titolo: Puntatori a funzioni membro non "static"

Ciao a tutti,
Immagino che quel che sto per chiedere faccia parte dei
fondamentali, ma non mi è chiaro lo stesso, anzi tocca un
punto chiave del linguaggio che non mi è mai stato chiaro
e potrebbe essere un'occasione per capirci qualcosa.
Il codice in calce, compila o meno a seconda che dichiari
"static" la funzione "fun". Mi pare di capire che in un
caso sta passando implicitamente "this" alla funzione
chiamata o qualcosa di simile. In realtà, questo codice è
una iper-semplice versione di qualcosa di più complesso
che nella fattispecie chiama a cascata 4 funzioni
passandogli due puntatori a funzioni proprie, che vengono
deferenziate solo nel livello più basso, il che forse
potrebbe essere utile ai fini di un'eventuale risposta.
Se dichiaro "static" una dozzina di routines nella mia
classe il tutto funziona ma non so cosa sto facendo e se è
una buona idea. In particolare, intuisco che potrei avere
malfunzionamenti se avessi istanze multiple della classe
(e potrebbe accadere). Non solo, ma all'interno della
classe vorrei usare il multithreading in modo abbastanza
intensivo. Date queste premesse, come devo fare per
utilizzare i puntatori a funzioni proprie in modo da non
creare problemi con istanze multiple e multithread?
Le funzioni che permettono o meno la compilazione se sono
o meno dichiarate "static" non accedono alle variabili
membro della classe ma mi piacerebbe lo stesso capire come
affrontare questo genere di problemi
Vi sarei grato di un consiglio perché dove sto ora non ho
la linea flat, ho solo un modem lento e sono lontano dai
libri. In rete gli unici esempi che trovo sono puntatori a
funzioni proprie dichiarati nel main program col relativo
costrutto, ma come potete vedere dal codice non è quello
che serve a me.
Grazie!
Apx.
// ......................... la classe
class Myclass
{
   private:
     /*static*/ void     fun (double) ;
     void     driver(void(*)(double)) ;
   public:
     void     exec () ;
} ;
// ............................ metodi
void    Myclass::fun (double arg)
{
   ;
}
void    Myclass::driver (void (*aarg)(double))
{
   aarg(0.0) ;
}
void    Myclass::exec ()
{
   driver (fun) ;
}
// .......................... il main
int main ()
{
   Myclass    cl ;
   (cl.exec()) ;
   return (0) ;
}

Ciao,
le funzioni statiche sono definite a livello di classe (anche se la
parola chiave "static" ha significati diversi a seconda del contesto,
e spesso questa è una ragione che motiva la confusione che suscita nei
neofiti), e quindi non possono accedere a membri dato o a metodi
propri di un'istanza della classe stessa, ma solo a membri dato
statici o altre funzioni membro statiche.
Grosso modo, le funzioni membro statiche sono vantaggiose da definire
nel corpo di una classe al posto che nello spazio globale perché:
* non inquinano lo spazio dei nomi globali (che sarebbe comunque
risolvibile con un namespace, ma questa è un'altra storia)
* molto più importante, hanno accesso a membri dato e funzioni
statiche private e protette della classe in cui sono definite,
mantenendo il principio di incapsulamento del codice.
Questo torna particolarmente utile per funzioni "helper" di una classe
che non richiedano accesso diretto a membri dati, o con il pattern
singleton (v. Wikipedia) che permette la costruzione di un'unica
istanza di una classe - solo per citare due esempi.
Quindi, una funzione membro di una classe, dichiarata come statica,
non è altro che una "normale" funzione nello spazio globale come in C,
fatto eccetto i punti di cui sopra, e puoi pensarla come tale se ti
aiuta a capire.
I puntatori a funzioni membro (NON statiche) hanno bisogno sicuramente
di due cose:
* un oggetto di invocazione
* il puntatore alla funzione vera e propria
Devi quindi passare ad una funzione che debba invocare un metodo
ambedue queste informazioni. La classe std::mem_fun dell'header STL
<memory> dovrebbe aiutarti per funzioni unarie o senza parametri
formali.
Altrimenti, se vuoi farlo a mano, un puntatore "aarg" a funzione
membro di Myclass si dichiara per esempio come:
void (Myclass::*aarg)(double).
Se prevedi un grande uso di puntatori a funzione, ti consiglio
l'impiego dell'ottima libreria sigc++ (http://
libsigc.sourceforge.net/
). Non è semplicissima da comprendere per un
neofita, ma è quella che permette di fare le cose migliori con lo
sforzo minore, imho, ed ha anche un certo supporto alla rientranza in
caso di programmi multithreaded. La documentazione per i funtori la
trovi sul sito sotto la sezione "Reference".
In genere, tuttavia, i puntatori a funzioni (membro e non) non sono
sempre utilissimi, a meno che non si usino gli algoritmi della
libreria standard come transform, etc. Il loro uso è perlopiù locale,
anche data la difficoltà di tenerli aggiornati e funzionanti in mezzo
al resto del codice. Chiediti quindi se non puoi riscrivere il tuo
codice per usare meglio l'ereditarietà tra classi e le funzioni
virtuali ed evitare (magari, del tutto) un uso pesante dei puntatori a
funzione.
Se usi molto il multithreading (beh, in realtà sempre), devi infatti
assicurarti che, per le funzioni non statiche, l'oggetto di
invocazione rimanga allocato finché necessario: se chiami un metodo di
un oggetto che è stato deallocato naturalmente ottieni un segmentation
fault. Di solito si alloca l'oggetto sullo heap e ci si assicura che
venga deallocato solo dopo che tutti i puntatori a metodi collegati
siano stati distrutti. Vedi anche la documentazione di std::auto_ptr,
può esserti di aiuto (ma non pretendere troppo da esso, e stai attento
leggendone la documentazione).
PS. non c'entra, ma prendi l'abitudine di definire come "const" i
metodi che non modificano i membri dati propri di un oggetto.
Spero sia d'aiuto,
Matteo

> Spero sia d'aiuto,
Grazie! Ti chiedo scusa del ritardo per i ringraziamenti
ma ho voluto leggere e rileggere. Sei stato molto gentile
a scrivere tanto, ho apprezzato ed è stato utilissimo.
ciao!
Apx.
-- 
"Ricordo la volta che mi hanno rapito: hanno mandato
un pezzo del mio dito a mio padre. Lui ha risposto
che voleva un'altra prova."