stringtranslate.com

Puntero de función

Un puntero de función , también llamado puntero de subrutina o puntero de procedimiento , es un puntero que hace referencia a código ejecutable, en lugar de a datos. Al desreferenciar el puntero de función, se obtiene la función a la que se hace referencia , que se puede invocar y se le pueden pasar argumentos como en una llamada de función normal. Este tipo de 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 hacerlo directamente a través de un identificador o dirección fijos.

Los punteros de función permiten ejecutar distintos códigos en tiempo de ejecución. También se pueden pasar a una función para habilitar devoluciones de llamadas .

Los punteros de función son compatibles con lenguajes de programación de tercera generación (como PL/I , COBOL , Fortran , [1] dBASE dBL [ aclaración necesaria ] y C ) y lenguajes de programación orientados a objetos (como C++ , C# y D ). [2]

Punteros de función simples

La implementación más simple de un puntero a función (o subrutina) es como una variable que contiene la dirección de la función dentro de la memoria ejecutable. Los lenguajes de tercera generación más antiguos , como PL/I y COBOL , así como los lenguajes más modernos, como Pascal y C, generalmente implementan punteros a función de esta manera. [3]

Ejemplo en C

El siguiente programa en C ilustra el uso de dos punteros de función:

#include <stdio.h>  /* para printf */ #include <string.h> /* para strchr */  doble cm_a_pulgadas ( doble cm ) { devolver cm / 2.54 ; }      // "strchr" es parte del manejo de cadenas de C (es decir, no necesita declaración) // Consulte https://en.wikipedia.org/wiki/Function_pointer/C_string_handling#Functionsint main ( void ) { double ( * func1 )( double ) = cm_a_pulgadas ; char * ( * func2 )( const char * , int ) = strchr ; printf ( "%f %s" , func1 ( 15.0 ), func2 ( "Wikipedia" , 'p' )); /* imprime "5.905512 pedia" */ return 0 ; }                

El siguiente programa utiliza un puntero de función para invocar una de dos funciones ( sino 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 function mainllame a function compute_sumdos veces, pasándole un puntero a la función de biblioteca sinla primera vez y un puntero a function cosla segunda vez. Function compute_suma su vez invoca una de las dos funciones indirectamente al desreferenciar su argumento de puntero de función funcpvarias 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 <matemática.h> #incluir <stdio.h> // Función que toma un puntero de función como argumentodoble calcular_suma ( doble ( * funcp )( doble ), doble lo , doble hi ) {        doble suma = 0.0 ;    // Agregar valores devueltos por la función apuntada '*funcp' entero yo ;  para ( i = 0 ; i <= 100 ; i ++ ) {         // Utilice el puntero de función 'funcp' para invocar la función doble x = i / 100.0 * ( hi - lo ) + lo ;            doble y = funcp ( x );    suma += y ;   } devuelve suma / 101.0 * ( hi - lo );       }doble cuadrado ( doble x ) {    devolver x * x ;   }int principal ( vacío ) {   doble suma ;  // Utilice la función de la biblioteca estándar 'sin()' como la función a la que se apunta suma = calcular_suma ( sin , 0.0 , 1.0 );     printf ( "suma(sin): %g \n " , suma );  // Utilice la función de la biblioteca estándar 'cos()' como la función a la que se apunta suma = suma_calculada ( cos , 0,0 , 1,0 );     printf ( "suma(cos): %g \n " , suma );  // Utilice la función definida por el usuario 'square()' como la función a la que se apunta suma = calcular_suma ( cuadrado , 0.0 , 1.0 );     printf ( "suma(cuadrado): %g \n " , suma );  devuelve 0 ; }

Funcionales

Los funtores, u objetos de función, son similares a los punteros de función y se pueden utilizar de forma similar. Un funtor es un objeto de un tipo de clase que implementa el operador de llamada de función , lo que permite que el objeto se utilice dentro de expresiones que utilizan la misma sintaxis que una llamada de función. Los funtores son más potentes que los punteros de función simples, 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, se puede implementar algo similar en este tipo de lenguajes, utilizando referencias a interfaces que definen un único método (función miembro). Los lenguajes CLI como C# y Visual Basic .NET implementan punteros de función de tipo seguro con delegados .

En otros lenguajes que admiten funciones de primera clase , las funciones se consideran datos y pueden pasarse, devolverse y crearse dinámicamente directamente por otras funciones, lo que elimina la necesidad de punteros de función.

El uso extensivo de punteros de función para llamar funciones puede producir una desaceleración del código en los procesadores modernos, porque un predictor de bifurcaciones puede no ser capaz de determinar hacia dónde bifurcarse (depende del valor del puntero de función en tiempo de ejecución), aunque este efecto puede ser exagerado ya que a menudo se compensa ampliamente con búsquedas en 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 denominados 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 sobre el que está operando, por lo que el tipo del objeto debe incluirse como parte del tipo del puntero de función. Luego, el método se utiliza en un objeto de esa clase mediante uno de los operadores de "puntero a miembro": .*o ->*(para un objeto o un puntero a objeto, respectivamente). [ dudosodiscutir ]

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 gruesos ", típicamente dos o tres veces el tamaño de un puntero de función simple, para poder lidiar con métodos virtuales y herencia virtual [ cita requerida ] .

En C++

En C++, además del método utilizado en C, también es posible utilizar la plantilla de clase de la biblioteca estándar de C++ std::function , cuyas instancias son objetos de función:

#include <iostream> #include <funcional> usando el espacio de nombres std ;    derivada doble estática ( función constante < double ( double ) > & f , double x0 , double eps ) { double eps2 = eps / 2 ; double lo = x0 - eps2 ; double hi = x0 + eps2 ; return ( f ( hi ) - f ( lo )) / eps ; }                                 estática doble f ( doble x ) { devolver x * x ; }        int main () { doble x = 1 ; cout << "d/dx(x ^ 2) [@ x = " << x << "] = " << derivada ( f , x , 1e-5 ) << finl ; devolver 0 ; }                     

Punteros a funciones miembro en C++

Así es como C++ utiliza punteros de función cuando trabaja con funciones miembro de clases o estructuras. Estas se invocan utilizando un puntero de objeto o una llamada this. Son de tipo seguro, ya que solo se pueden llamar miembros de esa clase (o derivados) utilizando un puntero de ese tipo. Este ejemplo también demuestra el uso de un typedef para el puntero a la función miembro agregado para simplificar. Los punteros de función a funciones miembro estáticas se realizan en el estilo tradicional de "C" porque no se requiere un puntero de objeto para la llamada this.

#include <iostream> usando el espacio de nombres std ;   clase Foo {  público : int add ( int i , int j ) { devuelve i + j ; } int mult ( int i , int j ) { devuelve i * j ; } static int negate ( int i ) { devuelve - i ; } };                          int bar1 ( int i , int j , Foo * pFoo , int ( Foo ::* pfn )( int , int )) { return ( pFoo ->* pfn )( i , j ); }          tipo definido int ( Foo ::* Foo_pfn )( int , int ); int bar2 ( int i , int j , Foo * pFoo , Foo_pfn pfn ) { return ( pFoo ->* pfn )( i , j ); }           typedef auto ( * PFN )( int ) -> int ; // Solo C++, igual que: typedef int(*PFN)(int);   int bar3 ( int i , PFN pfn ) { devolver pfn ( i ); }       int main () { Foo foo ; cout << "Foo::add(2,4) = " << bar1 ( 2 , 4 , & foo , & Foo :: add ) << finl ; cout << " Foo ::mult(3,5) = " << bar2 ( 3 , 5 , & foo , & Foo :: mult ) << finl ; cout << "Foo::negate(6) = " << bar3 ( 6 , & Foo :: negate ) << finl ; devolver 0 ; }                                

Sintaxis alternativa de C y C++

La sintaxis de C y C++ que se muestra arriba es la canónica que se utiliza en todos los libros de texto, pero es difícil de leer y explicar. Incluso los typedefejemplos anteriores utilizan esta sintaxis. Sin embargo, todos los compiladores de C y C++ admiten 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 typedefrealmente se puede utilizar es con un puntero, pero eso resalta su carácter de puntero.

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 ( char 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 puntero a 'Fn', y le asigna la dirección de 'F'. Fn * fn = & F ; // Tenga en cuenta que '&' no es obligatorio, pero resalta 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 ); } // Call(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 aún se puede usar primero; luego, el tipo original se puede definir en términos de él usando el nuevo estilo.// Esto define 'PFn', un tipo de puntero al tipo Fn. typedef Fn * PFn ;  // 'PFn' se puede usar dondequiera que se pueda usar 'Fn *' 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 Fnse puede utilizar en definiciones de punteros a funciones miembro:

// Esto define 'C', una clase con funciones estáticas y miembro similares, // y luego crea una instancia llamada 'c' class C { public : static int Static ( char c ); int Member ( 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 'Static' a 'fn'. // Dado que no hay ningún 'this', 'Fn' es el tipo correcto; y 'fn' se puede usar como se indicó anteriormente. fn = & C :: Static ;  // Esto define 'm', un puntero a un miembro de 'C' con tipo 'Fn', // y le asigna la dirección de 'C::Member'. // 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 :: Member ;   // 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 un miembro de 'C' de tipo 'Fn' y un 'char', // llama a 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', // llama a 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 aún se puede usar primero; luego, 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 la clase 'C' de tipo 'Fn' typedef Fn C ::* FnC ;  // 'FnC' se puede utilizar dondequiera que 'Fn C::*' pueda FnC fnC = & C :: Member ; int RefP ( C & p , FnC m , char c );         

Véase también

Referencias

  1. ^ Andrew J. Miller. "Fortran Examples" (Ejemplos de Fortran) . Consultado el 14 de septiembre de 2013 .
  2. ^ "Tutoriales sobre punteros de función". logo. Archivado desde el original el 16 de mayo de 2011. 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.
  3. ^ "Tutoriales sobre punteros de función". logo. Archivado desde el original el 2011-05-16 . Consultado el 2011-04-13 . 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.
  4. ^ "Experiencia: Lenguaje intermedio: C++: Uso de functor para devoluciones de llamadas en C++". DevX.com. 2005-01-31 . Consultado el 2011-04-13 . Si desea utilizar una función miembro como función de devolución de llamada, 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