Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


4.2.4c  Paso de funciones como argumento

§1  Sinopsis

Como se ha señalado ( 4.2.4b), la segunda (aunque no la menos importante) finalidad de los punteros a función, es pasar funciones como argumentos en las llamadas a otras funciones. Es necesario utilizar este artificio (punteros) porque en principio, la gramática de C++ no permite utilizar funciones en la declaración de parámetros ( 4.4.1).

En general el uso de estos punteros permite técnicas de programación bastante sofisticadas, y como hemos señalado anteriormente, la posibilidad de utilizar funciones como argumentos en la invocación de otras funciones, es la principal razón de la introducción en el lenguaje de este tipo de punteros. El fondo de la cuestión consiste en alargar la funcionalidad de la función invocada, ya que a través de sus argumentos, puede invocar otras funciones. El mecanismo puede esquematizarse como sigue:

void func(void (* fp)() ) {  // funcion aceptando un puntero-a-función fp
  ...    // computacion propia
  fp();  // acceso a la computación de la función señalada por fp
}

La utilización podría responder al siguiente esquema:

void func1() { /* cierto proceso */ }
void func(void (* fp)() ) { fp(); } // función aceptando un puntero-a-función
void (* fptr)();    // declaracion de puntero-a-funcion 
...
fptr = func1;       // fptr se inicia con la direccion de func1
func(fptr);         // func puede ejecutar el código de func1

Naturalmente, en un caso como el anterior, también es posible acceder directamente a func1 desde func sin necesidad de punteros:

void func1() { /* proceso de func1 */ }
void func() { func1(); }
func();      // Ok func ejecuta el código de func1

Sin embargo, la utilidad cobra su verdadero significado cuando el argumento no es constante (no se invoca siempre la misma función) sino que es una variable. La situación puede esquematizarse como sigue:

void func1() { /* proceso-1 */ }  // funciones que
void func2() { /* proceso-2 */ }  // efectúan
void func3() { /* proceso-3 */ }  // distintas
...
void funcn() { /* proceso-n */ }  // computaciones

void func(void (* fp)() ) { fp(); }  // función aceptando puntero-a-función
void (* fptr)();    // declaración de puntero-a-función 
fptr = ...     /* El valor del puntero depende de determinadas condiciones externas
por ejemplo, la información leída en una interfaz analógico-digital */

func(fptr);     // se ejecuta el código adecuado según el caso


§2
  Veamos un ejemplo compilable utilizando una mutación del código del ejemplo anterior ( 4.2.4b Ej.4).

#include <stdio.h>
#include <conio.h>
 
char auxa(int posicion);           // prototipo de auxa
void escribe (int posicion, char (* ptr)(int) ); // L.5: prototipo de escribe
 
int main() {                       // ======================
  char (* fptr) (int);             // M.1: declara fptr puntero-a-función

  fptr = auxa;                     // M.2: inicia fptr
  printf("La letra%2d es una: %c\n", 5, auxa(5));
  escribe (5, fptr);               // M.4: llamada a escribe
  return (0);
}
 
char auxa (int num) {              // Definición de auxa
  char * str = "Hola mundo";
  return str[num];                 // L.16
}
 
void escribe (int pos, char (* fpointer)(int) ) { // definición de escribe
  char ch ;
  printf ("Estoy recibiendo la letra: %c\n", auxa(pos));
  ch = (* fpointer)(6);
  printf ("Estoy inventando la letra: %c\n", ch); 
}

Salida:

La letra 5 es una: m
Estoy recibiendo la letra: m
Estoy inventando la letra: u

Comentario:

La función auxa recibe un entero y devuelve un carácter. El puntero str a la matriz de caracteres "Hola mundo" es una variable local de esta función. El lector observador, percibirá que el carácter devuelto en L.16, se obtiene utilizando una notación de puntero con subíndice, algo extraño a la luz de la expuesto hasta ahora. En realidad también podríamos haber puesto:

return  *(str+num);    // L.16-bis

Al tratar de las matrices ( 4.3.2) justificaremos la notación de subíndices más compacta. Por el momento el lector puede aceptar la que le resulte más cómoda. También observar que en este caso hemos suprimido el paréntesis en el valor devuelto por no ser realmente necesario.

return (str[num]) == return str[num]

La función escribe recibe dos parámetros: un entero, y un puntero-a-función aceptando un entero y devolviendo un carácter. Su prototipo está en L.5, y su definición en L.18. Observe que esta función proporciona dos salidas. La primera de ellas es una invocación a printf parecida a la realizada en M.3, en la que el segundo argumento es también el resultado de una invocación directa a auxa.

La segunda se realiza también mediante un printf, pero en este caso, se utiliza directamente un char como segundo argumento, que es proporcionado por la variable local ch. El punto interesante aquí es que su valor se ha obtenido mediante una invocación a auxa a través de un puntero.

El programa comienza declarando fptr, un puntero-a-función que acepta un entero y devuelve un char (M.1). A continuación se inicia de forma que señale a la función auxa (que cumple con las condiciones exigidas por el puntero).

En M.3 se escribe la primera salida.  Observe que el tercer argumento de printf es el valor devuelto por una invocación a auxa pasándole un 5 como argumento. En este contexto las sentencias

auxa(n);
(* fpointer)(n);
fpointer(n);

son equivalentes.

En M.4 se realiza una invocación a escribe utilizando como segundo argumento es el puntero fptr definido en las líneas anteriores.  Esta invocación es responsable de la segunda y tercera salidas.