studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones c++ c function pointers c++-faq

c++ - programacion - ¿Cuál es el punto de los indicadores de función?



manual de programacion android pdf (16)

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

¿Podría darnos algún ejemplo del buen uso de punteros a las funciones (en C o C ++)?


Bueno, generalmente los uso (profesionalmente) en tablas de salto (vea también esta pregunta de ).

Las tablas de salto se utilizan comúnmente (pero no exclusivamente) en máquinas de estado finito para que sean impulsadas por datos. En lugar de interruptor / caja anidados

switch (state) case A: switch (event): case e1: .... case e2: .... case B: switch (event): case e3: .... case e1: ....

puede hacer una matriz 2d o punteros a funciones y simplemente llamar a handleEvent[state][event]


Como dijo Rich anteriormente, es muy común que los punteros de funciones en Windows hagan referencia a alguna dirección que almacena la función.

Cuando programa en C language en la plataforma de Windows, básicamente carga algunos archivos DLL en la memoria primaria (usando LoadLibrary ) y para usar las funciones almacenadas en DLL necesita crear indicadores de funciones y señalar estas direcciones (usando GetProcAddress ).

Referencias


De acuerdo con todo lo anterior, más .... Cuando cargue dinámicamente un dll durante el tiempo de ejecución, necesitará punteros a funciones para llamar a las funciones.


Ejemplos:

  1. Clasificación / búsquedas personalizadas
  2. Diferentes patrones (como Estrategia, Observador)
  3. Devolución de llamada

El ejemplo "clásico" para la utilidad de los punteros de función es la función qsort() biblioteca C, que implementa una clasificación rápida. Para ser universal en todas y cada una de las estructuras de datos que el usuario pueda imaginar, se necesitan un par de punteros vacíos para ordenar los datos 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 ordenar ascendente o descendente.


En C, el uso clásico es la función qsort , donde el cuarto parámetro apunta a una función que se utilizará para realizar el orden dentro del género. En C ++, uno tendería a usar funtores (objetos que parecen funciones) para este tipo de cosas.


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 necesita en el tiempo de ejecución, se puede asignar una implementación diferente al puntero de función.


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ás escribiendo Bomberman. 5 segundos después de que la persona suelte la bomba, debería explotar (llamar a la función de explode() ).

Ahora hay 2 formas de hacerlo. Una forma es "probar" todas las bombas en la pantalla para ver si están listas para explotar en el ciclo principal.

foreach bomb in game if bomb.boomtime() bomb.explode()

Otra forma es adjuntar una devolución de llamada a su sistema de reloj. Cuando se planta una bomba, agregas una devolución de llamada para que se llame a bomb.explode () cuando sea el momento adecuado .

// user placed a bomb Bomb* bomb = new Bomb() make callback( function=bomb.explode, time=5 seconds ) ; // IN the main loop: foreach callback in callbacks if callback.timeToRun callback.function()

Aquí callback.function() puede ser cualquier función , porque es un puntero de función.


Para los lenguajes OO, 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 funcione para 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 extra de "si-más".

Permite muchas otras cosas útiles en Python como generadores, cierres, etc.


Un uso del puntero a la función podría ser donde no queramos modificar el código donde se llama a la función (lo que significa que la llamada puede ser condicional y bajo diferentes condiciones, necesitamos hacer un tipo diferente de procesamiento). Aquí los punteros de función son muy útiles, ya que no es necesario 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. El puntero de función se puede hacer para señalar funciones diferentes condicionalmente. (Esto puede hacerse 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 al que se llama (supongamos que es una API de biblioteca que no podemos modificar). La API usa un puntero de función para llamar a la función apropiada definida por el usuario.


Una perspectiva diferente, además de otras buenas respuestas aquí:

En C, solo hay punteros de funciones, no hay funciones.

Quiero decir, escribes funciones, pero no puedes manipular funciones. No hay representación en tiempo de ejecución de una función como tal. Ni siquiera puedes llamar "una función". Cuando escribes:

my_function(my_arg);

lo que en realidad estás diciendo es "realizar una llamada al puntero my_function con el argumento especificado". Estás haciendo una llamada a través de un puntero de función. Este puntero de desintegración a función significa que los siguientes comandos son equivalentes a la llamada a la función anterior:

(&my_function)(my_arg); (*my_function)(my_arg); (**my_function)(my_arg); (&**my_function)(my_arg); (***my_function)(my_arg);

y así sucesivamente (gracias @LuuVinhPhuc).

Entonces, ya está usando punteros a funciones como valores . Obviamente, usted querrá tener variables para esos valores, y aquí es donde entran todos los usos de otras cosas: 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 una clase std::function , pero el principio sigue siendo prácticamente el mismo.


Usé indicadores 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 me estoy ejecutando, necesita llamar a diferentes versiones de algunas funciones.

En el momento de la inicialización, el programa averigua en qué hardware se está ejecutando y rellena los punteros de función. Todas las rutinas de nivel superior en el programa solo 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 de interruptor / caja 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 todos lados.

También probé capas de funciones intermedias para descubrir qué función usar, pero no ayudaron mucho. Todavía tuve que actualizar las declaraciones de casos en varios lugares cada vez que añadimos una nueva variante. Con los punteros de función, solo tengo que cambiar la función de inicialización.


Utilizo los punteros de función extensivamente, 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.


Voy a ir contra la corriente 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 cualquiera de los punteros de función o funtores (objetos de función) para el mismo resultado.

Los funtores tienen una serie de ventajas sobre los punteros de función sin procesar, debido a su naturaleza de objeto, en particular:

  • Pueden presentar varias sobrecargas del operator()
  • Pueden tener estado / referencia a variables existentes
  • Se pueden construir en el lugar ( lambda y bind )

Yo personalmente prefiero los funtores a los punteros de función (a pesar del código repetitivo), sobre todo porque la sintaxis de los punteros a las funciones puede ponerse peluda fácilmente (del Tutorial de puntero de función ):

typedef float(*pt2Func)(float, float); // defines a symbol pt2Func, pointer to a (float, float) -> float function typedef int (TMyClass::*pt2Member)(float, char, char); // defines a symbol pt2Member, pointer to a (float, char, char) -> int function // belonging to the class TMyClass

La única vez que he visto los punteros de función utilizados donde los funtores no podían estar era en Boost.Spirit. Han abusado completamente de la sintaxis para pasar un número arbitrario de parámetros como un único parámetro de plantilla.

typedef SpecialClass<float(float,float)> class_type;

Pero dado que las plantillas variadic y lambdas están a la vuelta de la esquina, no estoy seguro de que usaremos los punteros de función en código C ++ puro durante mucho tiempo.


Uso del puntero de función

Para llamar a la función de forma dinámica en función de la entrada del usuario. Al crear un mapa de cadena y puntero de función en este caso.

#include<iostream> #include<map> using namespace std; //typedef map<string, int (*)(int x, int y) > funMap; #define funMap map<string, int (*)(int, int)> funMap objFunMap; int Add(int x, int y) { return x+y; } int Sub(int x, int y) { return x-y; } int Multi(int x, int y) { return x*y; } void initializeFunc() { objFunMap["Add"]=Add; objFunMap["Sub"]=Sub; objFunMap["Multi"]=Multi; } int main() { initializeFunc(); while(1) { string func; cout<<"Enter your choice( 1. Add 2. Sub 3. Multi) : "; int no, a, b; cin>>no; if(no==1) func = "Add"; else if(no==2) func = "Sub"; else if(no==3) func = "Multi"; else break; cout<<"/nEnter 2 no :"; cin>>a>>b; //function is called using function pointer based on user input //If user input is 2, and a=10, b=3 then below line will expand as "objFuncMap["Sub"](10, 3)" int ret = objFunMap[func](a, b); cout<<ret<<endl; } return 0; }

De esta forma, hemos utilizado el puntero de función en nuestro código de empresa actual. Puede escribir ''n'' número de función y llamarlos usando este método.

SALIDA:

Enter your choice( 1. Add 2. Sub 3. Multi) : 1 Enter 2 no :2 4 6 Enter your choice( 1. Add 2. Sub 3. Multi) : 2 Enter 2 no : 10 3 7 Enter your choice( 1. Add 2. Sub 3. Multi) : 3 Enter 2 no : 3 6 18


La mayoría de los ejemplos se reducen a las devoluciones de llamada : llama a una función f() pasa la dirección de otra función g() , y f() llama a g() para alguna tarea específica. Si pasa f() la dirección de h() , entonces f() llamará a h() lugar.

Básicamente, esta es una forma de parametrizar una función: alguna parte de su comportamiento no está codificada en f() , sino en la función de devolución de llamada. Las personas que llaman pueden hacer que f() comporte de manera diferente pasando diferentes funciones de devolución de llamada. Un clásico es qsort() de la biblioteca estándar C que toma su criterio de ordenació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 funtores). Estos son objetos que sobrecargan el operador de llamada de función, por lo que puede llamarlos como si fueran una función. Ejemplo:

class functor { public: void operator()(int i) {std::cout << "the answer is: " << i << ''/n'';} }; functor f; f(42);

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:

class functor { public: functor(const std::string& prompt) : prompt_(prompt) {} void operator()(int i) {std::cout << prompt_ << i << ''/n'';} private: std::string prompt_; }; functor f("the answer is: "); f(42);

Otra ventaja es que a veces es más fácil realizar llamadas en línea para objetos de función que llamadas a través de punteros de función. Esta es una de las razones por las que la clasificación en C ++ es a veces más rápida que la clasificación en C.