Newsgroup: it.comp.lang.c++
Titolo: puntatori a funzione

Durante la lettura del libro, mi sono imbattuto, nei puntatori a funzione. 
Qualcuno sarebbe cos'ì gentile da spiegarmi a cosa servono?
Grazie.
-- 
Abruzzo SIlvio
studente, 16 anni
goku_...@libero.it
silvi...@aruba.it
Linux Machine 219721
^_____^

Qualcuno sarebbe cos'ì gentile da spiegarmi a cosa servono?
Si tratta di un puntatore che invece di permetterti di
raggiungere dei dati o una istanza di un oggetto ti
permettono di puntare direttamente codice.
Un esempio dovrebbe chiarire...
  #include <iostream>
  void Funzione1( int x )
  {
    std::cout << "Funzione 1 chiamata con x=" << x << std::endl;
  }
  void Funzione2( int x )
  {
    std::cout << "Funzione 2 chiamata con x=" << x << std::endl;
  }
  int main()
  {
    void (*f)(int); // f e' un puntatore a funzione
    f = Funzione1;  // ora f sta puntando a Funzione1
    f(6502);        // chiama la funzione puntata
    f = Funzione2;  // ora f punta all'altra funzione
    f(42);          // chiama la funzione puntata
    return 0;
  }
HTH
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

 


 Qual'è la differenza di fare f(6502) o Funzione1(6502)?
In effetti Andrea ti ha spiegato cosa sono e non a cosa servono.
Il problema e' che non e' semplice spiegare a cosa servano, non
fraintendermi, sono molto utili, ma non e' per nulla facile fare esempi che
ne rendano chiara l'utilita' a chi non si e' ancora trovato di fronte ad un
problema che si risolve bene grazie all'uso di questa tecnica.
L'esempio migliore che mi viene in mente e' quello dell'uso di un array di
puntatori a funzione per gestire dei menu etc.. (anche per gestire i
messaggi che arrivano alla wndProc quando programmi in C per le Win32 API).
Facciamo l'esempio dei menu... (visto che non so se tu programmi con le
win32 API o meno):
Suppponi di avere una schermata che dice:
<SCHERMATA VIDEO>
Che 'funzione' vuoi svolgere:"
0) per creare un nuovo documento...
1) per aprire un documento precedentemente salvato...
2) per salvare il documento attuale...
3) per salvare con un altro nome il documento attuale...
.........................
.........................
</SCHERMATA VIDEO>
L'appricio tradizionale sarebbe questo:
<CODE1>
#include<iostream>
using namespace std;
bool Nuovo();
bool Apri();
bool Salva();
bool SalvaConNome();
int main()
{
     int index;
     cout << "Che 'funzione' vuoi svolgere:" << endl;
     cout << "0) per creare un nuovo documento..." << endl;
     cout << "1) per aprire un documento precedentemente salvato..." <<
endl;
     cout << "2) per salvare il documento attuale..." << endl;
     cout << "3) per salvare con un altro nome il documento attuale..." <<
endl;
     cin >> index;
     switch(index){
          case 0:
                    Nuovo();
                    break;
          case 1:
                    Apri();
                    break;
          case 2:
                    Salva();
                    break;
          case 3:
                    SalvaConNome();
                    break;
     }
     return 0;
}
bool Nuovo()
{
     cout << "Nuovo chiamato!" << endl;
     return true;
}
bool Apri()
{
     cout << "Apri chiamato!" << endl;
     return true;
}
bool Salva()
{
     cout << "Salva chiamato!" << endl;
     return true;
}
bool SalvaConNome()
{
     cout << "SalvaConNome chiamato!" << endl;
     return true;
}
</CODE1>
L'aspetto non e' molto grazioso, vero ?!? E' orrendo !!
(ma il vantaggio e' che e' molto semplice da capire, e' praticamente
istintivo!).
Con i puntatori a funzione invece la cosa viene ordinatissima ed elegante
stai a vedere:
<CODE2>
#include<iostream>
using namespace std;
bool Nuovo();
bool Apri();
bool Salva();
bool SalvaConNome();
struct AZIONE{
     bool (*pf)();
     char* str;
};
AZIONE myMenu[]={
     Nuovo,"per creare un nuovo documento...",
     Apri,"per aprire un documento precedentemente salvato...",
     Salva,"per salvare il documento attuale...",
     SalvaConNome,"per salvare con un altro nome il documento attuale..."
};
int main()
{
     int index;
     cout << "Che 'funzione' vuoi svolgere:" << endl;
     for(int i=0;i<sizeof(myMenu)/sizeof(AZIONE);++i)
          cout << i << ") " << myMenu[i].str << endl;
     cin >> index;
     myMenu[index].pf();
     return 0;
}
bool Nuovo()
{
     cout << "Nuovo chiamato!" << endl;
     return true;
}
bool Apri()
{
     cout << "Apri chiamato!" << endl;
     return true;
}
bool Salva()
{
     cout << "Salva chiamato!" << endl;
     return true;
}
bool SalvaConNome()
{
     cout << "SalvaConNome chiamato!" << endl;
     return true;
}
</CODE2>
WOW, fatto !!! Chiami il puntatore a funzione con indice index, tutto qui
!!!
Inoltre per modificare, aggiungere, cambiare, eliminare le voci del menu in
TUTTO il programma (puoi usarli anche dopo i puntatori a funzione) basta
modificare l'array all'inizio !!!
E' tutto molto bello, main contiene un cliclo ed una chiamata invece di
tutte quelle orribili print e quella sfilza di switch. Ora immagina di avere
15 menu nel tuo programma. Ognuno con almeno 10 voci. Inoltre supponi che
ogni singolo menu possa essere visualizzato in piu' parti del codice....
Decidi tu cosa utilizzare....
I puntatori a funzione ti permettono anche di poter cambiare il menu at
run-time e di inserire un abbozzo di polimorfismo nella programmazione
C-style !
Riguardo agli esempi: sono solo esempi, basta ad esempio digitare 4 per
mandare il tutto in tilt. Il codice e' ventuto gia' lungo cosi', era
ridicolo mettere dei controlli etc... E' solo un esempio!
Ad ogni modo sappi che i puntatori a funzione sono tipici del C. In C++ se
ne sente molto meno il bisogno e nel caso si cerca di ricorrere ai loro
figli in versione OOP: le funzioni virtuali !!!
Insomma se usi il C++ e la OOP forse non li userai molto spesso !!!
I puntatori a funzioni non si limitano a questo esempio, oltre ad un
primitivo polimorfismo (di cui gia' ti ho parlato) sono utilissimi in altre
situazioni!
Spero che tu, dall'esempio, riesca a cogliere l'eleganza e la bellezza dei
puntatori a funzione.
Ad esempio per aggiungere una voce al menu basta aggiugere questa riga di
codice nell'array:
Chiudi,"per uscire dal programma!"
Insomma, il ciclo stampa automaticamente la nuova voce (con indice 4). E
tale indice chiama automaticamente la funzione Chiudi(). (nel vecchio modo
bisognava andare fino nel main ed aggiungere una cout. Poi un case e gestire
la chiamata. Per non parlare del casino se volessimo cambiare l'ordine del
menu!!!).
NOTA:Per controllare che index non sia fuori dai valori basta usare la
stessa tecnica usata nel for !!!
Spero di averti aiutato, non e' facile capirli subito. Ti ho aiutato
volentieri in quanto io stesso, alla tua eta', feci parecchia fatica a
capirli !!!
Alex
P.S. fammi sapere....
(ah,e' quasi mezza notte, spero di non aver fatto errori !)

Alex wrote:

Grazie, ho capito benissimo. ^____^


Qual'è la differenza di fare f(6502) o Funzione1(6502)?
Nessuna. La funzione chiamata e' la stessa.
Il puntatore serve proprio a quello: ti permette di
chiamare una funzione usando f senza sapere di quale
funzione si tratta.
Un esempio un po' piu' sensato potrebbe essere questo:
  #include <iostream>
  double calc_max(double (*f)(double),
                  double low,double high,int steps)
  {
    double mx=0;
    for (int i=0; i<=steps; i++)
    {
      double x=low+i*(high-low)/steps;
      double y=f(x);
      if (i==0 || mx<y) mx=y;
    }
    return mx;
  }
  double fun1(double x) { return 1+x/2; }
  double fun2(double x) { return (x+1)*(x-1); }
  int main()
  {
    std::cout << "massimo di 1+x/2 = "
              << calc_max(fun1,0,1,100)
              << std::endl;
    std::cout << "massimo di (x+1)*(x-1) = "
              << calc_max(fun2,0,1,100)
              << std::endl;
    return 0;
  }
In questo caso la funzione calc_max calcola il massimo
di una funzione (usando un sistema a campionamento)
ricevendo come parametro la funzione di cui cercare
il massimo e l'intervallo su cui fare la ricerca.
Nel programma main vengono controllate due funzioni
diverse ed entrambe sono richiamate da calc_max
usando il puntatore a funzione f.
In C++ i puntatori a funzione non si usano moltissimo:
per questo tipo di lavoro di solito si usano degli oggetti
(che rispetto ai puntatori a funzione hanno il vantaggio
di potersi mantenere uno stato interno).


 di potersi mantenere uno stato interno).
che intendi con "stato interno"?
Grazie.


Silvio,
per favore limitati a citare quelle parti dei messaggi a cui intendi
rispondere direttamente. Cosi' ci eviti di scorrere decine di righe per
un "grazie".
Ciao,
Nicola

Nicola Musatti wrote:

Grazie, hai ragione :-)
