Newsgroup: it.comp.lang.c++
Titolo: Lippman e i puntatori

Io sto studiando su un libro i puntatori i C/C++: i concetti di base li ho
capiti, ma non mi sono chiari i puntatori a funzioni e il funzionamento
della funzione qsort. Nel libro di Lippman sono spiegati meglio questi
concetti? Qualcuno di voi più esperto può spiegarmi meglio la questione dei
puntatori a funzione, tenendo conto che sono alle prime armi col C/C++?
Grazie
--
San Vegeta from Hell
http://www.geocities.com/Area51/Dungeon/8436
http://come.to/Fantasy.it

...


 puntatori a funzione, tenendo conto che sono alle prime armi col C/C++?
Dunque. Un puntatore è una variabile atta a contenere l'indirizzo di un
oggetto, giusto? Esempio:
int n = 123;     // Una variabile che contiene il valore 123
int *p = &n;     // Un puntatore che contiene l'indirizzo della
variabile di cui sopra
Per ottenere il valore dell'oggetto puntato dal puntatore basta
utilizzare l'operatore di dereferenziazione. Esempio:
cout << *p << endl;
Il puntatore a funzione non è altro che un tipo particolare di puntatore
che, invece di puntare ad una variabile punta, appunto, ad una funzione.
Esempio:
int miaFunz( int n, char *p )
{
    ...
}
int ( *p )( int, char * ) = miaFunz;
Ora il puntatore p (puntatore a funzione che richiede un int e un char *
e restituisce un int) contiene l'indirizzo della funzione miaFunz. Per
lanciare la funzione attraverso il puntatore basta utilizzarlo
esattamente come il nome della funzione. Esempio:
p( 100, "Prova" );
Se hai altri dubbi scrivi.
EMG

puntatori a funzione, tenendo conto che sono alle prime armi col C/C++?
qsort() e` un approccio decisamente obsoleto in C++ (std::sort funziona
molto, ma MOLTO meglio!) quindi non credo proprio che Lippman e
Lajoie ci spendano sopra molto tempo; i puntatori a funzione, invece,
rivestono ancora la loro importanza (sia pure minore in C++ che in C),
quindi qualcosa dovrebbe esserci.
Un puntatore a funzione, se non nullo, indica in qualsiasi momento
una data funzione (VERA funzione, cioe` NON funzione membro di
oggetto; puo`, invece, essere anche una funzione static di una
qualche classe, questo e` ammesso); tutte le funzioni indicabili da
un dato ptr-a-funzione devono avere la stessa "signature", cioe`
gli stessi tipi di argomenti e di valore di ritorno.
Puoi usare il puntatore a funzione esattamente come useresti il
nome di una funzione, cioe` per CHIAMARE la funzione (nel
caso del puntatore: quella che esso indica in quel dato momento)
con certi argomenti (di giusto tipo) ed eventualmente ottenere e
usare il valore di ritorno.  La sintassi di chiamata e` identica:
    int x = pippo(y, z);
qui, sintatticamente, e` indifferente se pippo sia il nome di una
funzione, oppure un puntatore a funzione.  Naturalmente, se e`
un puntatore, puo` essere prudente testare se sia nullo prima
di usarlo per la chiamata (se no si rischia un crash di programma):
    int x;
    if(pippo)
        x = pippo(y,z);
    else
        x = 23;
L'utilita` dei puntatori a funzione si ha ogni volta che certe date
"chiamate" in un programma devono potersi trovare, in modo
automatico e trasparente, a invocare funzioni diverse a seconda
dei casi; le chiamate verranno allora effettuate su di un puntatore
a funzione, il quale verra` opportunamente settato a indicare la
funzione appropriata prima dell'esecuzione delle chiamate.
Notiamo che la stessa utilita` e` fornita dal meccanismo dei
virtual (polimorfismo), e infatti, dovunque usiamo un puntatore
a funzione, potremmo, almeno in teoria, usare invece un
puntatore ad una classe-base astratta con un opportuno
metodo virtuale; l'uso di una classe base astratta apre poi un
universo di ulteriori possibilita`, ed e` appunto per questo che
l'importanza dei puntatori-a-funzione in C++ e` tanto minore
di quanto non fosse invece in C (dove i virtual non esistono).
L'esistenza dei template (come usati ad esempio in std::sort)
riduce ulteriormente la "nicchia ecologica" dei ptr-a-funzione.
Alex

>puntatori a funzione, tenendo conto che sono alle prime armi col C/C++?


 puntatori a funzione, tenendo conto che sono alle prime armi col C/C++?
Dunque. Un puntatore è una variabile atta a contenere l'indirizzo di un
oggetto, giusto? Esempio:
int n = 123;     // Una variabile che contiene il valore 123
int *p = &n;     // Un puntatore che contiene l'indirizzo della
variabile di cui sopra
Per ottenere il valore dell'oggetto puntato dal puntatore basta
utilizzare l'operatore di dereferenziazione. Esempio:
cout << *p << endl;
Fin qui tutto ok
Il puntatore a funzione non è altro che un tipo particolare di puntatore
che, invece di puntare ad una variabile punta, appunto, ad una funzione.
Esempio:
int miaFunz( int n, char *p )
{
    ...
}
Questa è una dichiarazione di funzione o il prototipo?
int ( *p )( int, char * ) = miaFunz;
Ecco: sul mio libro non si dice nulla al riguardo. *p ha qualcosa a che fare
col char *p che sta nei parametri della funzione? Praticamente col puntatore
a funzione mi risparmio di scrivere il nome della funzione e basta ??? Forse
è meglio che approfondiamo un po' il discorso, altrimenti non capirò mai per
bene la cosa.
Ora il puntatore p (puntatore a funzione che richiede un int e un char *
e restituisce un int) contiene l'indirizzo della funzione miaFunz. Per
lanciare la funzione attraverso il puntatore basta utilizzarlo
esattamente come il nome della funzione. Esempio:
p( 100, "Prova" );
Se hai altri dubbi scrivi.
L'ho appena fatto :-)
EMG

funzioni non vedo l'utilità...
Il puntatore verra` utile tutte e sole le volte che il codice deve
richiamare funzioni diverse in diversi casi (se risulta piu` comodo
delle varie ottime alternative, come polimorfismo, template, ecc;
tieni presente che in C, da cui vengono i puntatori a funzione al
C++, queste alternative non esistevano).
Ad esempio, supponi che io abbia scritto un semplice algoritmo
di integrazione numerica (perdonamene la banale semplicita`,
visto che lo scopo e` meramente espositivo):
    double epsilon = (fine-inizio)/(numpassi - 1);
    double totale = 0.0;
    for(int i=0; i<numpassi; ++i)
        totale += f(inizio + i * epsilon);
    return totale * epsilon;
Come dovrei "incapsulare" questo algoritmo in una funzione?
Specificamente, che parametri dovrebbe prendere questa
funzione "integrale"?  Certamente inizio e fine, gli estremi di
integrazione, e magari numpassi, il numero di passi da usare;
ma, come fare per f?  Come posso fare ad usare questa
funzione, in diversi punti del mio programma, per integrare
DIVERSE funzioni f...?
Se f fosse il nome di una funzione, allora questo algoritmo
non sarebbe riusabile: dovrei riscriverlo piu` e piu` volte per
ogni funzione che mi interessa integrare.  Che spreco!  Puo`
non sembrare tanto sprecoso per un algoritmo cosi`
semplice, ma se usassimo un BUON algoritmo per il calcolo
numerico dell'integrale definito non ce la caveremmo certo
con cinque righe, ti assicuro:-).  E allora?  Come facciamo?
Osservato che il problema e` che quella chiamata ad "f"
deve chiamare funzioni diverse in diversi casi, risolviamo
brillantemente usando, appunto, i puntatori a funzione:
    // punt_a_funz e` il tipo di un puntatore-a-funzione, che
    // indica una funzione che prende double e torna double:
    typedef double (*punt_a_funz)(double);
    double integrale(punt_a_funz f, double inizio, double fine, int numpassi)
    {
        // il corpo che abbiamo scritto sopra
    }
Adesso, da altri punti del codice, io posso liberamente chiamare
    double area = integrale(tan, 0.1, 0.4, 400);
in un punto, poi magari
    double altra = integrale(exp, -1.2, 3.5, 2000);
in un altro, e cosi` via.  I NOMI di funzione che uso qua (e potrei,
naturalmente, anche usare il nome di una funzione scritta da me
e parte del mio programma, purche`:
    1. sia una VERA funzione (globale, static, static di una classe)
        e NON un "metodo" (funzione non-static membro di una
        classe di oggetti)
    2. abbia un singolo argomento double e un risultato double
) sono usati come "indirizzo" delle funzioni stesse, e passati
quindi alla integrale() come puntatori-a-funzione (punt_a_funz,
specificamente).
Piu` chiaro adesso?
Alex

 funzioni non vedo l'utilità...
Servono quando non sai a tempo di compilazione quale funzione dovrà
essere chiamata ma lo saprai solo a run-time.
EMG

 a funzione mi risparmio di scrivere il nome della funzione e basta ???
No. Il puntatore a funzione serve fondamentalmente a chiamare funzioni
non stabilite a tempo di compilazione ma solo a run-time. Un po' come le
funzioni virtuali delle classi.
EMG