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.4 Puntero a función

§1 Sinopsis

Los punteros a función son uno de los recursos más potentes y flexibles de C/C++, permitiendo técnicas de programación muy eficientes. Por ejemplo, escribir funciones que manejan diferentes tipos de datos; diseñar algoritmos muy compactos ("function dispatchers"), que pueden sustituir largas cadenas if...else o switch ( 4.10.2), o alterar el flujo de ejecución del programa, modificando el orden de llamadas a funciones en base a determinadas prioridades ("adaptive program flow"). Así mismo, resultan de gran ayuda en programas de simulación y modelado. Sin embargo, los autores están generalmente de acuerdo en que constituyen para el principiante uno de los puntos más confusos del lenguaje. A esto se une que el tema suele ser despachado en una par de páginas como mucho. Por lo general, exponiendo el asunto con un ejemplo cuya lógica es de por sí bastante difícil de seguir. Tómese como ejemplo el de Kernighan y Ritchie ( K&R) que por lo demás, quizás sea el libro más conciso y mejor escrito sobre C.

En mi modesta opinión, parte de culpa de la confusión reside en la complejidad de la notación utilizada para estos menesteres. En consecuencia, aconsejo al lector preste especial atención a este aspecto y repase las Reglas de lectura ( Apéndice 6.1).

§2 La "dirección" de las funciones:

Para hablar de punteros a funciones, previamente hay que establecer que las funciones tengan "dirección". Del mismo modo que en una matriz se asume que su dirección es la del primer elemento, se asume también que la dirección de una función es la del segmento de código ("Code segment" 1.3.2) donde comienza el código de dicha función [1]. Es decir, la dirección de memoria a que se transfiere el control cuando se la invoca (su punto de comienzo).

Una vez establecido esto, no tiene que extrañar que puedan definirse variables de un tipo especial para apuntar a estas direcciones. Técnicamente un puntero-a-función es una variable que guarda la dirección de comienzo de la función. Pero como tendremos ocasión de comprobar, la mejor manera de pensar en ellos es considerarlos como una especie de "alias" de la función, aunque con una importante cualidad añadida: que pueden ser utilizados como argumentos de otras funciones.

Por supuesto que los punteros a función se utilizan en último extremo para acceder a la función señalada (las funciones existen para ser invocadas). Pero en general, cuando se utiliza este recurso, es muy frecuente que en alguno de los pasos intermedios se utilicen tales funciones como argumentos de otras. De hecho puede decirse que los punteros a funciones son en realidad un artificio de C++ para poder utilizar funciones como argumentos de otras funciones, dado que la gramática de C++ no permite en principio utilizar funciones en la declaración de parámetros ( 4.4.1). Por lo demás, no está permitida ninguna operación de aritmética (de punteros) con ellos [6].

Nota: en lo que respecta a las clases, pueden definirse punteros a funciones miembro ( 4.2.1g), pero generalmente tales punteros solo se utilizan para acceder a métodos estáticos ( 4.11.7), ya que para acceder a los miembros de clases en general no se utilizan punteros, sino un operador especial [3], el operador de resolución de ámbito :: ( 4.9.19). Por otra parte, no es posible definir punteros a los constructores o destructores de clase, ya que son un tipo especial de funciones miembro de las que no puede obtenerse su dirección ( 4.11.2d).


§3 Un puntero a función es una variable del tipo denominado: "puntero-a-función recibiendo A argumentos y devolviendo X", donde A son los argumentos que recibe la función y X es el tipo de objeto devuelto. Cada una de las infinitas combinaciones posibles da lugar a un tipo específico de puntero-a-función.

Considere detenidamente las declaraciones de los ejemplos siguientes (en todos ellos fptr es un puntero a función de tipo distinto de los demás). Observe una característica que se repite: el nombre del puntero está siempre entre paréntesis. Insistiremos en esta singularidad más adelante ( 4.2.4a) al tratar de la declaración de estos punteros.

void (*fptr)(); fptr es un puntero a una función, sin parámetros, que devuelve void.
void (*fptr)(int); fptr es un puntero a función que recibe un int como parámetro y devuelve void.
int (*fptr)(int, char); fptr es puntero a función, que acepta un int y un char como argumentos y devuelve un int.
int* (*fptr)(int*, char*); fptr es puntero a función, que acepta sendos punteros a int y char como argumentos, y devuelve un puntero a int.
void (_USERENTRY * fptr)(void); fptr es puntero a función, sin parámetros que devuelve void y utiliza la convención de llamada _USERENTRY [4]
LONG (PASCAL * lpfnWndProc)(); lpfnWndProc es puntero a función, sin parámetros que devuelve LONG y utiliza la convención de llamada PASCAL [5].
Cuando el valor devuelto por la función es a su vez un puntero (a función, o de cuaquier otro tipo), la notación se complica un poco más:
int const * (*fptr)(); fptres un puntero a función que no recibe argumentos y devuelve un puntero a un int constante
float (*(*fptr)(char))(int); fptr es un puntero a función que recibe un char como argumento y devuelve un puntero a función que recibe un int como argumento y devuelve un float.
void * (*(*fptr)(int))[5]; fptr es un puntero a función que recibe un int como argumento y devuelve un puntero a un array de 5 punteros-a-void (genéricos).
char (*(*fptr)(int, float))(); fptr es un puntero a función que recibe dos argumentos (int y float), devolviendo un puntero a función que no recibe argumentos y devuelve un char.
long (*(*(*fptr)())[5])(); fptr es un puntero a función que no recibe argumentos y devuelve un puntero a un array de 5 punteros a función que no reciben ningún parámetro y devuelven long.
§4 Matrices de punteros a función

Los punteros a función también pueden agruparse en matrices. Por ejemplo:

int (* afptr[10])(int);    // matriz de 10 punteros a función


De la propia definición se desprende que en estas matrices, todos los punteros señalan funciones que devuelven el mismo tipo de valor y reciben los mismos tipos de parámetros. En el ejemplo anterior, la variable afptr es declarada como matriz de 10 punteros a función que reciben int y devuelven int. Más sobre matrices de punteros en ( 4.3.5).

Observe que la gramática C++ acepta la existencia de matrices de punteros-a-función, que resultan muy útiles, pero no la existencia de matrices de funciones. Sin embargo, como veremos más adelante, en la práctica son equivalentes; para casi todos los efectos una función puede sustituirse por su puntero. Estas matrices permiten invocar funciones (a través de sus punteros) utilizando los miembros de la matriz mediante notación de subíndices, lo que da mucho juego en determinadas circunstancias. Por ejemplo, el caso anterior permitiría invocaciones del tipo:

int z = afptr[n](x);


  Inicio.


[1]  Es interesante señalar esta diferencia genérica: los punteros a funciones señalan a direcciones de código, mientras que los punteros a variables o constantes señalan a direcciones a datos. Puede encontrar más completos comentarios sobre este punto, en el artículo de Danny Kalev  "Can Two Functions Have the Same Address?".

[3] Esta falta de homogeneidad en la notación de acceso es una de las características que más le reprochan a C++ sus detractores.

[4] Tomado de la definición de atexit (&1.5.1)

[5] Se trata de una declaración típica de los entornos de programación para Windows. En los compiladores C++ para estos entornos, las constantes manifiestas LONG y PASCAL están previamente definidas como long y __pascal ( 4.4.6a) respectivamente.

[6]  Siguiendo con la argumentación de la nota [1] diríamos que no tiene mucho sentido realizar operaciones aritméticas con direcciones de código.