Puntero de función - Function pointer
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 , dBASE dBL y C ) y lenguajes de programación orientados a objetos (como C ++ , C # y D ).
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.
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 <stdio.h> /* for printf */
#include <string.h> /* for strchr */
double cm_to_inches(double cm) {
return cm / 2.54;
}
// "strchr" is part of the C string handling (i.e., no need for declaration)
// See https://en.wikipedia.org/wiki/C_string_handling#Functions
int main(void) {
double (*func1)(double) = cm_to_inches;
char * (*func2)(const char *, int) = strchr;
printf("%f %s", func1(15.0), func2("Wikipedia", 'p'));
/* prints "5.905512 pedia" */
return 0;
}
El siguiente programa usa un puntero de función para invocar una de dos funciones ( sino cos) indirectamente desde otra función ( compute_sumcalculando una aproximación de la integración de Riemann de la función ). El programa opera haciendo que la función mainllame a la función compute_sumdos veces, pasándole un puntero a la función de biblioteca sinla primera vez y un puntero para que funcione cosla segunda vez. La función, compute_suma su vez, invoca una de las dos funciones indirectamente desreferenciando 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 <math.h>
#include <stdio.h>
// Function taking a function pointer as an argument
double compute_sum(double (*funcp)(double), double lo, double hi) {
double sum = 0.0;
// Add values returned by the pointed-to function '*funcp'
int i;
for (i = 0; i <= 100; i++) {
// Use the function pointer 'funcp' to invoke the function
double x = i / 100.0 * (hi - lo) + lo;
double y = funcp(x);
sum += y;
}
return sum / 101.0 * (hi - lo);
}
double square(double x) {
return x * x;
}
int main(void) {
double sum;
// Use standard library function 'sin()' as the pointed-to function
sum = compute_sum(sin, 0.0, 1.0);
printf("sum(sin): %g\n", sum);
// Use standard library function 'cos()' as the pointed-to function
sum = compute_sum(cos, 0.0, 1.0);
printf("sum(cos): %g\n", sum);
// Use user-defined function 'square()' as the pointed-to function
sum = compute_sum(square, 0.0, 1.0);
printf("sum(square): %g\n", sum);
return 0;
}
Functors
Los funciones, u objetos de función, son similares a los punteros de función y pueden usarse 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.
Muchos lenguajes orientados a objetos "puros" no admiten punteros de función. Sin embargo, algo similar se puede implementar en este tipo de lenguajes, usando 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 .
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 <functional>
static double derivative(const std::function<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;
}
static double f(double x) {
return x * x;
}
int main() {
double x = 1;
std::cout << "d/dx(x ^ 2) [@ x = " << x << "] = " << derivative(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 tipo 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 <iostream>
using namespace 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::add(2,4) = " << bar1(2,4, &foo, &Foo::add) << endl;
cout << "Foo::mult(3,5) = " << bar2(3,5, &foo, &Foo::mult) << endl;
cout << "Foo::negate(6) = " << bar3(6, &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 typedefejemplos 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 typedefrealmente se puede usar es con un puntero, pero eso resalta su puntería.
C y C ++
// This declares 'F', a function that accepts a 'char' and returns an 'int'. Definition is elsewhere.
int F(char c);
// This defines 'Fn', a type of function that accepts a 'char' and returns an 'int'.
typedef int Fn(char c);
// This defines 'fn', a variable of type pointer-to-'Fn', and assigns the address of 'F' to it.
Fn *fn = &F; // Note '&' not required - but it highlights what is being done.
// This calls 'F' using 'fn', assigning the result to the variable 'a'
int a = fn('A');
// This defines 'Call', a function that accepts a pointer-to-'Fn', calls it, and returns the result
int Call(Fn *fn, char c) {
return fn(c);
} // Call(fn, c)
// This calls function 'Call', passing in 'F' and assigning the result to 'call'
int call = Call(&F, 'A'); // Again, '&' is not required
// LEGACY: Note that to maintain existing code bases, the above definition style can still be used first;
// then the original type can be defined in terms of it using the new style.
// This defines 'PFn', a type of pointer-to-type-Fn.
typedef Fn *PFn;
// 'PFn' can be used wherever 'Fn *' can
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 función de puntero a miembro:
// This defines 'C', a class with similar static and member functions,
// and then creates an instance called 'c'
class C {
public:
static int Static(char c);
int Member(char c);
} c; // C
// This defines 'p', a pointer to 'C' and assigns the address of 'c' to it
C *p = &c;
// This assigns a pointer-to-'Static' to 'fn'.
// Since there is no 'this', 'Fn' is the correct type; and 'fn' can be used as above.
fn = &C::Static;
// This defines 'm', a pointer-to-member-of-'C' with type 'Fn',
// and assigns the address of 'C::Member' to it.
// You can read it right-to-left like all pointers:
// "'m' is a pointer to member of class 'C' of type 'Fn'"
Fn C::*m = &C::Member;
// This uses 'm' to call 'Member' in 'c', assigning the result to 'cA'
int cA = (c.*m)('A');
// This uses 'm' to call 'Member' in 'p', assigning the result to 'pA'
int pA = (p->*m)('A');
// This defines 'Ref', a function that accepts a reference-to-'C',
// a pointer-to-member-of-'C' of type 'Fn', and a 'char',
// calls the function and returns the result
int Ref(C &r, Fn C::*m, char c) {
return (r.*m)(c);
} // Ref(r, m, c)
// This defines 'Ptr', a function that accepts a pointer-to-'C',
// a pointer-to-member-of-'C' of type 'Fn', and a 'char',
// calls the function and returns the result
int Ptr(C *p, Fn C::*m, char c) {
return (p->*m)(c);
} // Ptr(p, m, c)
// LEGACY: Note that to maintain existing code bases, the above definition style can still be used first;
// then the original type can be defined in terms of it using the new style.
// This defines 'FnC', a type of pointer-to-member-of-class-'C' of type 'Fn'
typedef Fn C::*FnC;
// 'FnC' can be used wherever 'Fn C::*' can
FnC fnC = &C::Member;
int RefP(C &p, FnC m, char c);
Ver también
Referencias
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
- Función de puntero tutoriales , una guía para C / C ++ punteros de función, las devoluciones de llamada , y los objetos de función (funtores)
- 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
- El libro C , punteros de funciones en C de "The C Book"
- Punteros de función en dBASE dBL , Puntero de función en dBASE dBL