Un puntero de función , también llamado puntero de subrutina o puntero de procedimiento , es un puntero que apunta a una función. A diferencia de hacer referencia a un valor de datos, un puntero de función apunta a un código ejecutable dentro de la memoria. Desreferenciar el puntero de función produce la función a la que se hace referencia , que se puede invocar y pasar argumentos como en una llamada de función normal. Esta invocación también se conoce como llamada "indirecta", porque la función se invoca indirectamente a través de una variable en lugar de directamente a través de un identificador fijo o una dirección.
Los punteros de función se pueden utilizar para simplificar el código proporcionando una forma sencilla de seleccionar una función para ejecutar en función de los valores de tiempo de ejecución.
Los punteros de función son compatibles con lenguajes de programación de tercera generación (como PL / I , COBOL , Fortran , [1] dBASE dBL y C ) y lenguajes de programación orientados a objetos (como C ++ , C # y D ). [2]
Punteros de función simple
La implementación más simple de un puntero de función (o subrutina) es como una variable que contiene la dirección de la función dentro de la memoria ejecutable. Los lenguajes más antiguos de tercera generación como PL / I y COBOL , así como los lenguajes más modernos como Pascal y C, generalmente implementan punteros de función de esta manera. [3]
Ejemplo en C
El siguiente programa en C ilustra el uso de dos punteros de función:
- func1 toma un parámetro de doble precisión (doble) y devuelve otro doble, y se asigna a una función que convierte centímetros a pulgadas.
- func2 toma un puntero a una matriz de caracteres constantes, así como un número entero y devuelve un puntero a un carácter, y se asigna a una función de manejo de cadenas C que devuelve un puntero a la primera aparición de un carácter dado en una matriz de caracteres.
#include / * para printf * /#include / * para strchr * /doble cm_to_inches ( doble cm ) { return cm / 2.54 ; }// "strchr" es parte del manejo de cadenas C (es decir, no es necesaria una declaración) // Ver https://en.wikipedia.org/wiki/C_string_handling#Functionsint main ( void ) { double ( * func1 ) ( doble ) = cm_to_inches ; char * ( * func2 ) ( const char * , int ) = strchr ; printf ( "% f% s" , func1 ( 15.0 ), func2 ( "Wikipedia" , 'p' )); / * imprime "5.905512 pedia" * / return 0 ; }
El siguiente programa usa un puntero de función para invocar una de dos funciones ( sin
o cos
) indirectamente desde otra función ( compute_sum
calculando una aproximación de la integración de Riemann de la función ). El programa opera haciendo que la función main
llame a la función compute_sum
dos veces, pasándole un puntero a la función de biblioteca sin
la primera vez y un puntero para que funcione cos
la segunda vez. La función, compute_sum
a su vez, invoca una de las dos funciones indirectamente desreferenciando su argumento de puntero de función funcp
varias veces, sumando los valores que devuelve la función invocada y devolviendo la suma resultante. Las dos sumas se escriben en la salida estándar mediante main
.
#include #include // Función tomando un puntero de función como argumentodouble compute_sum ( double ( * funcp ) ( double ), double lo , double hi ) { suma doble = 0.0 ; // Agregar valores devueltos por la función apuntada '* funcp' int i ; para ( i = 0 ; i <= 100 ; i ++ ) { // Usa el puntero de función 'funcp' para invocar la función doble x = i / 100,0 * ( hi - Mín ) + Mín ; doble y = funcp ( x ); suma + = y ; } return sum / 101.0 * ( hi - lo );}doble cuadrado ( doble x ) { return x * x ;}int main ( void ) { doble suma ; // Usa la función de biblioteca estándar 'sin ()' como función apuntada suma = suma_cálculo ( sin , 0.0 , 1.0 ); printf ( "suma (pecado):% g \ n " , suma ); // Usa la función de biblioteca estándar 'cos ()' como función apuntada suma = suma_cálculo ( cos , 0.0 , 1.0 ); printf ( "suma (cos):% g \ n " , suma ); // Usa la función definida por el usuario 'square ()' como función apuntada suma = suma_cálculo ( cuadrado , 0.0 , 1.0 ); printf ( "suma (cuadrado):% g \ n " , suma ); return 0 ;}
Functors
Los funciones, u objetos de función, son similares a los punteros de función y se pueden usar de manera similar. Un functor es un objeto de un tipo de clase que implementa el operador de llamada a función , lo que permite que el objeto se use dentro de expresiones que usan la misma sintaxis que una llamada a función. Los functors son más poderosos que los simples punteros de función, ya que pueden contener sus propios valores de datos y permiten al programador emular cierres . También se utilizan como funciones de devolución de llamada si es necesario utilizar una función miembro como función de devolución de llamada. [4]
Muchos lenguajes orientados a objetos "puros" no admiten punteros de función. Sin embargo, algo similar se puede implementar en este tipo de lenguajes, utilizando referencias a interfaces que definen un solo método (función miembro). Los lenguajes CLI como C # y Visual Basic .NET implementan punteros de función con seguridad de tipos con delegados .
En otros lenguajes que admiten funciones de primera clase , las funciones se consideran datos y pueden ser pasados, devueltos y creados dinámicamente directamente por otras funciones, eliminando la necesidad de punteros de función.
El uso extensivo de punteros de función para llamar a funciones puede producir una ralentización del código en los procesadores modernos, porque es posible que el predictor de rama no pueda averiguar a dónde bifurcar (depende del valor del puntero de función en tiempo de ejecución) aunque esto El efecto puede ser exagerado, ya que a menudo se compensa ampliamente mediante búsquedas de tablas no indexadas significativamente reducidas.
Punteros de método
C ++ incluye soporte para programación orientada a objetos , por lo que las clases pueden tener métodos (generalmente referidos como funciones miembro). Las funciones miembro no estáticas (métodos de instancia) tienen un parámetro implícito (el puntero this ) que es el puntero al objeto en el que está operando, por lo que el tipo de objeto debe incluirse como parte del tipo del puntero de función. Luego, el método se usa en un objeto de esa clase usando uno de los operadores "puntero a miembro": .*
o ->*
(para un objeto o un puntero a objeto, respectivamente).
Aunque los punteros de función en C y C ++ se pueden implementar como direcciones simples, por lo general sizeof(Fx)==sizeof(void *)
, los punteros de miembro en C ++ a veces se implementan como "punteros gordos", típicamente dos o tres veces el tamaño de un puntero de función simple, para tratar con virtual métodos y herencia virtual [ cita requerida ] .
En C ++
En C ++, además del método utilizado en C, también es posible utilizar la función std :: function de la plantilla de clase de biblioteca estándar de C ++ , cuyas instancias son objetos de función:
#include #include derivada doble estática ( const std :: función < doble ( doble ) > & f , doble x0 , doble eps ) { doble eps2 = eps / 2 ; doble lo = x0 - eps2 ; doble hi = x0 + eps2 ; return ( f ( hi ) - f ( lo )) / eps ; } static double f ( doble x ) { return x * x ; }int main () { doble x = 1 ; std :: cout << "d / dx (x ^ 2) [@ x =" << x << "] =" << derivada ( f , x , 1e-5 ) << std :: endl ; return 0 ; }
Punteros a funciones miembro en C ++
Así es como C ++ usa punteros de función cuando se trata de funciones miembro de clases o estructuras. Estos se invocan mediante un puntero de objeto o una llamada a this. Son seguros de tipos en el sentido de que solo puede llamar a miembros de esa clase (o derivados) utilizando un puntero de ese tipo. Este ejemplo también demuestra el uso de typedef para la función de puntero a miembro agregada para simplificar. Los punteros de función a las funciones miembro estáticas se realizan en el estilo tradicional 'C' porque no se requiere un puntero de objeto para esta llamada.
#include usando el espacio de nombres std ;class Foo {public : int add ( int i , int j ) { return i + j ; } int mult ( int i , int j ) { return i * j ; } static int negate ( int i ) { return - i ; } };int bar1 ( int i , int j , Foo * pFoo , int ( Foo :: * pfn ) ( int , int )) { return ( pFoo -> * pfn ) ( i , j ); }typedef int ( Foo :: * Foo_pfn ) ( int , int );int bar2 ( int i , int j , Foo * pFoo , Foo_pfn pfn ) { return ( pFoo -> * pfn ) ( i , j ); }typedef int ( * PFN ) ( int );int bar3 ( int i , PFN pfn ) { return pfn ( i ); }int main () { Foo foo ; cout << "Foo :: agregar (2,4) =" << bar1 ( 2 , 4 , & foo , & Foo :: agregar ) << endl ; cout << "Foo :: mult (3,5) =" << bar2 ( 3 , 5 , & foo , & Foo :: mult ) << endl ; cout << "Foo :: negate (6) =" << bar3 ( 6 , y Foo :: negate ) << endl ; return 0 ; }
Sintaxis alternativa de C y C ++
La sintaxis de C y C ++ dada anteriormente es la canónica que se usa en todos los libros de texto, pero es difícil de leer y explicar. Incluso los typedef
ejemplos anteriores usan esta sintaxis. Sin embargo, cada compilador de C y C ++ admite un mecanismo más claro y conciso para declarar punteros de función: use typedef
, pero no almacene el puntero como parte de la definición. Tenga en cuenta que la única forma en que este tipo de typedef
realmente se puede usar es con un puntero, pero eso resalta su puntería.
C y C ++
// Esto declara 'F', una función que acepta un 'char' y devuelve un 'int'. La definición está en otra parte. int F ( carácter c );// Esto define 'Fn', un tipo de función que acepta un 'char' y devuelve un 'int'. typedef int Fn ( char c );// Esto define 'fn', una variable de tipo pointer-to-'Fn ', y le asigna la dirección de' F '. Fn * fn = & F ; // Tenga en cuenta que '&' no es obligatorio, pero destaca lo que se está haciendo.// Esto llama a 'F' usando 'fn', asignando el resultado a la variable 'a' int a = fn ( 'A' );// Esto define 'Call', una función que acepta un puntero a 'Fn', lo llama y devuelve el resultado int Call ( Fn * fn , char c ) { return fn ( c ); } // Llamar (fn, c)// Esto llama a la función 'Call', pasando 'F' y asignando el resultado a 'call' int call = Call ( & F , 'A' ); // Nuevamente, '&' no es obligatorio// LEGADO: Tenga en cuenta que para mantener las bases de código existentes, el estilo de definición anterior todavía se puede usar primero; // entonces el tipo original se puede definir en términos de él usando el nuevo estilo.// Esto define 'PFn', un tipo de puntero a tipo Fn. typedef Fn * PFn ;// 'PFn' se puede usar siempre que 'Fn *' pueda PFn pfn = F ; int CallP ( PFn fn , char c );
C ++
Estos ejemplos utilizan las definiciones anteriores. En particular, tenga en cuenta que la definición anterior de Fn
se puede utilizar en definiciones de función de puntero a miembro:
// Esto define 'C', una clase con funciones miembro y estáticas similares, // y luego crea una instancia llamada 'c' class C { public : static int Static ( char c ); int Miembro ( char c ); } c ; // C// Esto define 'p', un puntero a 'C' y le asigna la dirección de 'c' C * p = & c ;// Esto asigna un puntero a 'Estático' a 'fn'. // Como no hay 'this', 'Fn' es el tipo correcto; y 'fn' se puede usar como arriba. fn = & C :: Estático ;// Esto define 'm', un puntero-a-miembro-de-'C 'con el tipo' Fn ', // y le asigna la dirección de' C :: Miembro '. // Puede leerlo de derecha a izquierda como todos los punteros: // "'m' es un puntero a un miembro de la clase 'C' de tipo 'Fn'" Fn C :: * m = & C :: Miembro ;// Esto usa 'm' para llamar a 'Miembro' en 'c', asignando el resultado a 'cA' int cA = ( c . * M ) ( 'A' );// Esto usa 'm' para llamar a 'Miembro' en 'p', asignando el resultado a 'pA' int pA = ( p -> * m ) ( 'A' );// Esto define 'Ref', una función que acepta una referencia a 'C', // un puntero-a-miembro-de-'C 'de tipo' Fn ', y un' char ', // llamadas la función y devuelve el resultado int Ref ( C & r , Fn C :: * m , char c ) { return ( r . * m ) ( c ); } // Ref (r, m, c)// Esto define 'Ptr', una función que acepta un puntero a 'C', // un puntero a miembro-de-'C 'de tipo' Fn 'y un' char ', // llamadas la función y devuelve el resultado int Ptr ( C * p , Fn C :: * m , char c ) { return ( p -> * m ) ( c ); } // Ptr (p, m, c)// LEGADO: Tenga en cuenta que para mantener las bases de código existentes, el estilo de definición anterior todavía se puede usar primero; // entonces el tipo original se puede definir en términos de él usando el nuevo estilo.// Esto define 'FnC', un tipo de puntero-a-miembro-de-clase-'C 'de tipo' Fn ' typedef Fn C :: * FnC ;// 'FnC' se puede usar siempre que 'Fn C :: *' pueda FnC fnC = & C :: Miembro ; int RefP ( C & p , FnC m , carácter c );
Ver también
- Delegación (informática)
- Objeto de función
- Función de orden superior
- Parámetro de procedimiento
Referencias
- ^ Andrew J. Miller. "Ejemplos de Fortran" . http://www.esm.psu.edu/~ajm138/fortranexamples.html . Consultado el 14 de septiembre de 2013 .Mantenimiento de CS1: ubicación ( enlace )
- ^ "Tutoriales del puntero de función" . http://www.newty.de/ : logotipo . Consultado el 13 de abril de 2011 .
Los punteros de función son punteros, es decir, variables, que apuntan a la dirección de una función.
- ^ "Tutoriales del puntero de función" . http://www.newty.de/ : logotipo . Consultado el 13 de abril de 2011 .
Nota importante: un puntero de función siempre apunta a una función con una firma específica. Por lo tanto, todas las funciones que desee utilizar con el mismo puntero de función deben tener los mismos parámetros y tipo de retorno.
- ^ "Experiencia: Lenguaje intermedio: C ++: Utilice Functor para devoluciones de llamada en C ++" . http://www.devx.com/ : DevX.com. 2005-01-31 . Consultado el 13 de abril de 2011 .
Si desea utilizar una función miembro como función de devolución de llamada, entonces la función miembro debe estar asociada con un objeto de la clase antes de poder llamarla. En este caso, puede utilizar functor [con un ejemplo en esta página].
enlaces externos
- Preguntas frecuentes sobre punteros de función , cosas que se deben evitar con punteros de función, información sobre el uso de objetos de función
- Tutoriales de puntero de función , una guía de punteros de función C / C ++, devoluciones de llamada y objetos de función (functors)
- Punteros de función de miembro y los delegados de C ++ más rápidos posibles , artículo de CodeProject de Don Clugston
- Tutoriales de puntero , documentación y tutoriales de C ++
- Los punteros C explicaron una guía visual de punteros en C
- Puntero de función segura y devoluciones de llamada en la programación de Windows , artículo de CodeProject de R. Selvam
- The C Book , punteros de funciones en C de "The C Book"
- Punteros de función en dBASE dBL , Puntero de función en dBASE dBL