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 datos. Al eliminar la referencia al puntero de función se obtiene la función referenciada , 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 o dirección fijos.

Los punteros de función permiten ejecutar código diferente 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 y C ) y lenguajes de programación orientados a objetos (como C++ , C# y D ). [2]

Punteros de funciones simples

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:

#include <stdio.h>  /* para printf */ #include <string.h> /* para strchr */  doble cm_to_inches ( doble cm ) { retorno 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/Function_pointer/C_string_handling#Functionsint principal ( vacío ) { doble ( * func1 ) ( doble ) = 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 funciona llamando a mainla función compute_sumdos veces, pasándole un puntero a la función de la biblioteca sinla primera vez y un puntero a la función cosla segunda vez. La función, compute_suma su vez, invoca una de las dos funciones indirectamente al eliminar la referencia a 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.

#incluir <matemáticas.h> #incluir <stdio.h> // Función que toma un puntero de función como argumentodoble cálculo_sum ( doble ( * funcp ) ( doble ), doble bajo , doble alto ) {        doble suma = 0,0 ;    // Agrega valores devueltos por la función apuntada '*funcp' int yo ;  para ( yo = 0 ; yo <= 100 ; yo ++ ) {         // Usa el puntero de función 'funcp' para invocar la función doble x = i / 100.0 * ( hola - baja ) + baja ;            doble y = funcp ( x );    suma += y ;   } suma de retorno / 101.0 * ( hola - baja );       }doble cuadrado ( doble x ) {    devolver x * x ;   }int principal ( vacío ) {   doble suma ;  // Utiliza la función de biblioteca estándar 'sin()' como función apuntada suma = suma_calculada ( pecado , 0,0 , 1,0 );     printf ( "suma(pecado): %g \n " , suma );  // Utiliza la función de biblioteca estándar 'cos()' como función apuntada suma = suma_calculada ( cos , 0,0 , 1,0 );     printf ( "suma(cos): %g \n " , suma );  // Utiliza la función definida por el usuario 'cuadrado()' como función apuntada suma = suma_calculada ( cuadrado , 0,0 , 1,0 );     printf ( "suma(cuadrado): %g \n " , suma );  devolver 0 ; }

Functores

Los functores, u objetos de función, son similares a los punteros de función y pueden usarse de manera 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 use dentro de expresiones que usan la misma sintaxis que una llamada de función. Los functores 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 "puros" orientados a objetos 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 otras funciones pueden pasarlas, devolverlas y crearlas dinámicamente directamente, 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 es posible que un predictor de bifurcación no pueda determinar a dónde bifurcarse (depende del valor del puntero de función en tiempo de ejecución), aunque Este efecto puede exagerarse, ya que a menudo se compensa ampliamente con una reducción significativa de las búsquedas en tablas no indexadas.

Consejos 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 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 de "puntero a miembro": .*o ->*(para un objeto o un puntero a objeto, respectivamente). [ dudoso ]

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 ", generalmente dos o tres veces el tamaño de un puntero de función simple, para poder manejar direcciones virtuales. 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 plantilla de clase de 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 const < doble ( doble ) > & f , doble x0 , doble eps ) { doble eps2 = eps / 2 ; doble lo = x0 - eps2 ; doble alto = x0 + eps2 ; retorno ( f ( hola ) - f ( bajo )) / eps ; }                                 estática doble f ( doble x ) { return x * x ; }        int principal () { doble x = 1 ; cout << "d/dx(x ^ 2) [@ x = " << x << "] = " << derivada ( f , x , 1e-5 ) << endl ; devolver 0 ; }                     

Punteros a funciones miembro en C++

Así es como C++ usa punteros de función cuando trata con funciones miembro de clases o estructuras. Estos se invocan mediante un puntero de objeto o una llamada this. Son seguros en el sentido de que sólo puedes llamar a miembros de esa clase (o derivados) usando un puntero de ese tipo. Este ejemplo también demuestra el uso de una definición de tipo para el puntero a la función miembro agregada por simplicidad. Los punteros de función a funciones miembro estáticas se realizan en el estilo tradicional 'C' porque no se requiere ningún puntero de objeto para esta llamada.

#include <iostream> usando el espacio de nombres std ;   clase Foo {  público : int agregar ( int i , int j ) { return i + j ; } int mult ( int i , int j ) { return i * j ; } static int negar ( 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 auto ( * PFN )( int ) -> int ; // Sólo C++, igual que: typedef int(*PFN)(int);   int bar3 ( int i , PFN pfn ) { return pfn ( i ); }       int principal () { Foo foo ; cout << "Foo::add(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 , & Foo :: negar ) << endl ; devolver 0 ; }                                

Sintaxis alternativa de C y C++

La sintaxis de C y C++ proporcionada anteriormente es la canónica utilizada en todos los libros de texto, pero es difícil de leer y explicar. Incluso los typedefejemplos anteriores utilizan 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 typedefse puede usar este tipo de información es con un puntero, pero eso resalta su carácter 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 ( 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 puntero a 'Fn', y le asigna la dirección de 'F'. Fn * fn = & F ; // La nota '&' no es obligatoria, 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 ); } // Llamada(fn,c)        // Esto llama a la función 'Call', pasando 'F' y asignando el resultado a 'call' int call = Call ( & F , 'A' ); // De nuevo, '&' no es necesario     // 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; // 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. definición de tipo Fn * PFn ;  // 'PFn' se puede utilizar 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 Fnse puede utilizar en definiciones de funciones de puntero a 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 ); miembro int ( 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 existe 'esto', 'Fn' es el tipo correcto; y 'fn' se puede utilizar como se indica arriba. fn = & C :: Estático ;  // Esto define 'm', un puntero a miembro de 'C' con tipo 'Fn', // y le asigna la dirección de 'C::Miembro'. // Puedes leerlo de derecha a izquierda como todos los punteros: // "'m' es un puntero al 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, metro, 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 aún 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 utilizar siempre que 'Fn C::*' pueda FnC fnC = & C :: Miembro ; int RefP ( C & p , FnC m , char c );         

Ver también

Referencias

  1. ^ Andrew J. Miller. "Ejemplos de Fortran" . Consultado el 14 de septiembre de 2013 .
  2. ^ "Tutoriales sobre el puntero de función". 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.
  3. ^ "Tutoriales sobre el puntero de función". 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.
  4. ^ "Experiencia: lenguaje intermedio: C++: uso de Functor para devoluciones de llamada en C++". 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, puedes usar funtor [con un ejemplo en esta página].

enlaces externos