Ponteiro de função - Function pointer

Um ponteiro de função , também chamado de ponteiro de sub - rotina ou ponteiro de procedimento , é um ponteiro que aponta para uma função. Ao contrário de fazer referência a um valor de dados, um ponteiro de função aponta para o código executável na memória. Desreferenciar o ponteiro de função produz a função referenciada , que pode ser chamada e argumentos transmitidos exatamente como em uma chamada de função normal. Essa chamada também é conhecida como chamada "indireta", porque a função está sendo chamada indiretamente por meio de uma variável, em vez de diretamente por meio de um identificador ou endereço fixo.

Ponteiros de função podem ser usados ​​para simplificar o código, fornecendo uma maneira simples de selecionar uma função a ser executada com base em valores de tempo de execução.

Ponteiros de função são suportados por linguagens de programação de terceira geração (como PL / I , COBOL , Fortran , dBASE dBL e C ) e linguagens de programação orientadas a objetos (como C ++ , C # e D ).

Ponteiros de função simples

A implementação mais simples de um ponteiro de função (ou sub-rotina) é como uma variável que contém o endereço da função na memória executável. Linguagens de terceira geração mais antigas , como PL / I e COBOL , bem como linguagens mais modernas, como Pascal e C, geralmente implementam ponteiros de função dessa maneira.

Exemplo em C

O programa C a seguir ilustra o uso de dois ponteiros de função:

  • func1 recebe um parâmetro de precisão dupla (duplo) e retorna outro duplo, e é atribuído a uma função que converte centímetros em polegadas.
  • func2 pega um ponteiro para uma matriz de caractere constante, bem como um inteiro e retorna um ponteiro para um caractere, e é atribuída a uma função de tratamento de string C que retorna um ponteiro para a primeira ocorrência de um determinado caractere em uma 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;
}

O próximo programa usa um ponteiro de função para invocar uma das duas funções ( sinou cos) indiretamente de outra função ( compute_sum, calculando uma aproximação da integração de Riemann da função ). O programa opera tendo a função de mainchamada de função compute_sumduas vezes, passando um ponteiro para a função de biblioteca sinna primeira vez e um ponteiro para a função cosna segunda vez. A função, compute_sumpor sua vez, invoca uma das duas funções indiretamente, desreferenciando seu argumento de ponteiro de função funcpvárias vezes, adicionando os valores que a função invocada retorna e retornando a soma resultante. As duas somas são gravadas na saída padrão por 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;
}

Functores

Functores, ou objetos de função, são semelhantes a ponteiros de função e podem ser usados ​​de maneiras semelhantes. Um functor é um objeto de um tipo de classe que implementa o operador de chamada de função , permitindo que o objeto seja usado em expressões que usam a mesma sintaxe de uma chamada de função. Functores são mais poderosos do que simples ponteiros de função, sendo capazes de conter seus próprios valores de dados e permitindo ao programador emular fechamentos . Eles também são usados ​​como funções de retorno de chamada se for necessário usar uma função de membro como uma função de retorno de chamada.

Muitas linguagens orientadas a objetos "puras" não suportam ponteiros de função. Porém, algo semelhante pode ser implementado nesses tipos de linguagens, usando referências a interfaces que definem um único método (função de membro). Linguagens CLI , como C # e Visual Basic .NET, implementam ponteiros de função de tipo seguro com delegados .

Em outras linguagens que oferecem suporte a funções de primeira classe , as funções são consideradas dados e podem ser passadas, retornadas e criadas dinamicamente diretamente por outras funções, eliminando a necessidade de ponteiros de função.

O uso extensivo de ponteiros de função para chamar funções pode produzir uma desaceleração do código em processadores modernos, porque o preditor de ramificação pode não ser capaz de descobrir para onde ramificar (depende do valor do ponteiro de função em tempo de execução), embora isso O efeito pode ser exagerado, pois geralmente é amplamente compensado por pesquisas de tabela não indexadas significativamente reduzidas.

Indicadores de método

C ++ inclui suporte para programação orientada a objetos , portanto, as classes podem ter métodos (geralmente chamados de funções-membro). Funções de membro não estáticas (métodos de instância) têm um parâmetro implícito (o ponteiro this ) que é o ponteiro para o objeto no qual está operando, portanto, o tipo do objeto deve ser incluído como parte do tipo do ponteiro de função. O método é então usado em um objeto dessa classe usando um dos operadores "ponteiro para membro": .*ou ->*(para um objeto ou um ponteiro para objeto, respectivamente).

Embora ponteiros de função em C e C ++ possam ser implementados como endereços simples, de modo que sizeof(Fx)==sizeof(void *), normalmente , ponteiros de membro em C ++ às vezes são implementados como "ponteiros de gordura", normalmente duas ou três vezes o tamanho de um ponteiro de função simples, a fim de lidar com o virtual métodos e herança virtual .

Em C ++

Em C ++, além do método usado em C, também é possível usar o template de classe std :: function da biblioteca padrão C ++ , das quais as instâncias são objetos de função:

#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;
}

Ponteiros para funções-membro em C ++

É assim que C ++ usa ponteiros de função ao lidar com funções-membro de classes ou estruturas. Eles são invocados usando um ponteiro de objeto ou uma chamada this. Eles são seguros para tipos, pois você só pode chamar membros dessa classe (ou derivados) usando um ponteiro desse tipo. Este exemplo também demonstra o uso de um typedef para o ponteiro para a função de membro adicionado para simplificar. Ponteiros de função para funções de membro estático são feitos no estilo 'C' tradicional porque não há nenhum ponteiro de objeto necessário para esta chamada.

#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;
}

Sintaxe alternativa C e C ++

A sintaxe C e C ++ fornecida acima é a canônica usada em todos os livros - mas é difícil de ler e explicar. Mesmo os typedefexemplos acima usam essa sintaxe. No entanto, todo compilador C e C ++ suporta um mecanismo mais claro e conciso para declarar ponteiros de função: use typedef, mas não armazene o ponteiro como parte da definição. Observe que a única maneira que este tipo de typedefrealmente pode ser usado é com um ponteiro - mas isso destaca o ponteiro dele.

C e 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 ++

Esses exemplos usam as definições acima. Em particular, observe que a definição acima para Fnpode ser usada em definições de função de ponteiro para membro:

// 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);

Veja também

Referências

links externos