Tengo problemas para ver la utilidad de los punteros de función. Supongo que puede ser útil en algunos casos (existen, después de todo), pero no puedo pensar en un caso en el que sea mejor o inevitable usar un puntero de función.
¿Podría dar algún ejemplo del buen uso de punteros de función (en C o C ++)?
Respuestas:
La mayoría de los ejemplos se reducen a devoluciones de llamada : usted llama a una función
f()
pasando la dirección de otra funcióng()
yf()
llamag()
a una tarea específica. Si pasaf()
la dirección de en suh()
lugar,f()
volverá a llamarh()
.Básicamente, esta es una forma de parametrizar una función: una parte de su comportamiento no está codificada
f()
en la función de devolución de llamada, sino en la función de devolución de llamada. Las personas que llaman puedenf()
comportarse de manera diferente pasando diferentes funciones de devolución de llamada. Un clásico esqsort()
de la biblioteca estándar de C que toma su criterio de clasificación como un puntero a una función de comparación.En C ++, esto se hace a menudo usando objetos de función (también llamados functores). Se trata de objetos que sobrecargan el operador de llamada a la función, por lo que puedes llamarlos como si fueran una función. Ejemplo:
La idea detrás de esto es que, a diferencia de un puntero de función, un objeto de función puede transportar no solo un algoritmo, sino también datos:
Otra ventaja es que a veces es más fácil hacer llamadas en línea a objetos de función que las llamadas a través de punteros de función. Esta es una razón por la que ordenar en C ++ es a veces más rápido que ordenar en C.
fuente
Bueno, generalmente los uso (profesionalmente) en tablas de salto (consulte también esta pregunta de StackOverflow ).
Las tablas de salto se usan comúnmente (pero no exclusivamente) en máquinas de estados finitos para hacerlas controladas por datos. En lugar de interruptor / caja anidados
puede hacer una matriz 2d de punteros de función y simplemente llamar
handleEvent[state][event]
fuente
Ejemplos:
fuente
std::sort
elcomp
parámetro como una estrategiaEl ejemplo "clásico" de la utilidad de los punteros de función es la
qsort()
función de biblioteca C , que implementa una clasificación rápida. Para ser universal para todas y cada una de las estructuras de datos que se le ocurran al usuario, se necesitan un par de punteros vacíos a datos ordenables y un puntero a una función que sepa cómo comparar dos elementos de estas estructuras de datos. Esto nos permite crear nuestra función de elección para el trabajo y, de hecho, incluso permite elegir la función de comparación en tiempo de ejecución, por ejemplo, para clasificar ascendente o descendente.fuente
De acuerdo con todo lo anterior, más ... Cuando carga un dll dinámicamente en tiempo de ejecución, necesitará punteros de función para llamar a las funciones.
fuente
Voy a ir a contracorriente aquí.
En C, los punteros de función son la única forma de implementar la personalización, porque no hay OO.
En C ++, puede usar punteros de función o functores (objetos de función) para el mismo resultado.
Los functores tienen una serie de ventajas sobre los punteros de función en bruto, debido a su naturaleza de objeto, en particular:
operator()
lambda
ybind
)Personalmente, prefiero functores a punteros de función (a pesar del código repetitivo), principalmente porque la sintaxis de los punteros de función puede complicarse fácilmente (del Tutorial de puntero de función ):
La única vez que he visto usar punteros de función donde los functors no podían fue en Boost.Spirit. Han abusado completamente de la sintaxis para pasar un número arbitrario de parámetros como un solo parámetro de plantilla.
Pero dado que las plantillas variadas y lambdas están a la vuelta de la esquina, no estoy seguro de que usemos punteros de función en código C ++ puro por mucho tiempo.
fuente
bind
ofunction
usa punteros de función. Es como decir que no usamos punteros en C ++ porque usamos punteros inteligentes. De todos modos, soy quisquilloso.En C, el uso clásico es la función qsort , donde el cuarto parámetro es el puntero a una función que se utilizará para realizar el ordenamiento dentro del ordenamiento. En C ++, uno tendería a usar functores (objetos que parecen funciones) para este tipo de cosas.
fuente
Usé punteros de función recientemente para crear una capa de abstracción.
Tengo un programa escrito en C puro que se ejecuta en sistemas integrados. Admite múltiples variantes de hardware. Dependiendo del hardware en el que esté ejecutando, necesita llamar a diferentes versiones de algunas funciones.
En el momento de la inicialización, el programa determina en qué hardware se está ejecutando y llena los punteros de función. Todas las rutinas de nivel superior del programa simplemente llaman a las funciones a las que hacen referencia los punteros. Puedo agregar soporte para nuevas variantes de hardware sin tocar las rutinas de nivel superior.
Solía usar declaraciones switch / case para seleccionar las versiones de función adecuadas, pero esto se volvió poco práctico a medida que el programa creció para admitir más y más variantes de hardware. Tuve que agregar declaraciones de casos por todas partes.
También probé capas de funciones intermedias para averiguar qué función usar, pero no ayudaron mucho. Todavía tenía que actualizar las declaraciones de casos en varios lugares cada vez que agregamos una nueva variante. Con los punteros de función, solo tengo que cambiar la función de inicialización.
fuente
Como Rich dijo anteriormente, es muy habitual que los punteros de funciones en Windows hagan referencia a alguna dirección que almacena funciones.
Cuando programa en la
C language
plataforma Windows, básicamente carga algún archivo DLL en la memoria primaria (usandoLoadLibrary
) y para usar las funciones almacenadas en DLL necesita crear punteros de funciones y apuntar a estas direcciones (usandoGetProcAddress
).Referencias:
LoadLibrary
GetProcAddress
fuente
Los punteros de función se pueden usar en C para crear una interfaz contra la cual programar. Dependiendo de la funcionalidad específica que se necesite en tiempo de ejecución, se puede asignar una implementación diferente al puntero de función.
fuente
Mi uso principal de ellos ha sido CALLBACKS: cuando necesitas guardar información sobre una función para llamar más tarde .
Digamos que está escribiendo Bomberman. 5 segundos después de que la persona arroje la bomba, debería explotar (llamar a la
explode()
función).Ahora hay 2 formas de hacerlo. Una forma es "sondear" todas las bombas en la pantalla para ver si están listas para explotar en el bucle principal.
Otra forma es adjuntar una devolución de llamada a su sistema de reloj. Cuando se coloca una bomba, agrega una devolución de llamada para que llame a bomb.explode () cuando sea el momento adecuado .
Aquí
callback.function()
puede haber cualquier función , porque es un puntero de función.fuente
Uso de puntero de función
Para llamar a la función de forma dinámica en función de la entrada del usuario. Creando un mapa de puntero de cadena y función en este caso.
De esta manera hemos utilizado el puntero de función en nuestro código de empresa real. Puede escribir 'n' número de función y llamarlos usando este método.
SALIDA:
fuente
Una perspectiva diferente, además de otras buenas respuestas aquí:
En C, solo usa punteros de función, no funciones (directamente).
Quiero decir, escribes funciones, pero no puedes manipular funciones. No hay una representación en tiempo de ejecución de una función como tal que pueda utilizar. Ni siquiera puedes llamar a "una función". Cuando escribes:
lo que en realidad está diciendo es "realizar una llamada al
my_function
puntero con el argumento especificado". Estás haciendo una llamada a través de un puntero de función. Esta disminución al puntero de función significa que los siguientes comandos son equivalentes a la llamada de función anterior:y así sucesivamente (gracias @LuuVinhPhuc).
Entonces, ya está usando punteros de función como valores . Obviamente, querrá tener variables para esos valores, y aquí es donde entran todos los usos de otras meciones: polimorfismo / personalización (como en qsort), devoluciones de llamada, tablas de salto, etc.
En C ++ las cosas son un poco más complicadas, ya que tenemos lambdas y objetos con
operator()
, e incluso unastd::function
clase, pero el principio sigue siendo prácticamente el mismo.fuente
(&my_function)(my_arg)
,(*my_function)(my_arg)
,(**my_function)(my_arg)
,(&**my_function)(my_arg)
,(***my_function)(my_arg)
... porque las funciones decaen a los punteros de funciónPara los lenguajes OO, para realizar llamadas polimórficas detrás de escena (esto también es válido para C hasta cierto punto, supongo).
Además, son muy útiles para inyectar un comportamiento diferente a otra función (foo) en tiempo de ejecución. Eso hace que la función sea una función de orden superior. Además de su flexibilidad, eso hace que el código foo sea más legible, ya que le permite sacar esa lógica adicional de "si-si no".
Permite muchas otras cosas útiles en Python como generadores, cierres, etc.
fuente
Utilizo punteros de función extensamente para emular microprocesadores que tienen códigos de operación de 1 byte. Una matriz de 256 punteros de función es la forma natural de implementar esto.
fuente
Un uso del puntero de función podría ser cuando no deseemos modificar el código donde se llama a la función (lo que significa que la llamada podría ser condicional y, en diferentes condiciones, necesitamos hacer un tipo diferente de procesamiento). Aquí los punteros de función son muy útiles, ya que no necesitamos modificar el código en el lugar donde se llama a la función. Simplemente llamamos a la función usando el puntero de función con los argumentos apropiados. Se puede hacer que el puntero de función apunte a diferentes funciones de forma condicional. (Esto se puede hacer en algún lugar durante la fase de inicialización). Además, el modelo anterior es muy útil, si no estamos en posición de modificar el código donde se llama (supongamos que es una API de biblioteca que no podemos modificar). La API utiliza un puntero de función para llamar a la función definida por el usuario adecuada.
fuente
Intentaré dar una lista algo completa aquí:
Devolución de llamada : personalice algunas funciones (de la biblioteca) con el código proporcionado por el usuario. El ejemplo principal es
qsort()
, pero también útil para manejar eventos (como un botón que llama a una devolución de llamada cuando se hace clic), o necesario para iniciar un hilo (pthread_create()
).Polimorfismo : la vtable en una clase C ++ no es más que una tabla de punteros de función. Y un programa en C también puede optar por proporcionar una tabla virtual para algunos de sus objetos:
El constructor de
Derived
luego establecería suvtable
variable miembro en un objeto global con las implementaciones dedestruct
y de la clase derivadafrobnicate
, y el código que necesitaba destruir unastruct Base*
simplemente llamaríabase->vtable->destruct(base)
, que llamaría a la versión correcta del destructor, independientemente de a qué clase derivadabase
apunta realmente .Sin punteros de función, el polimorfismo debería codificarse con un ejército de construcciones de interruptores como
Esto se vuelve bastante difícil de manejar con bastante rapidez.
Código cargado dinámicamente : cuando un programa carga un módulo en la memoria e intenta llamar a su código, debe pasar por un puntero de función.
Todos los usos de punteros de función que he visto caen directamente en una de estas tres amplias clases.
fuente