Tenho problemas para ver a utilidade dos ponteiros de função. Acho que pode ser útil em alguns casos (eles existem, afinal), mas não consigo pensar em um caso em que seja melhor ou inevitável usar um ponteiro de função.
Você poderia dar algum exemplo de bom uso de ponteiros de função (em C ou C ++)?
Respostas:
A maioria dos exemplos resumem-se a retornos de chamada : você chama uma função
f()
passando o endereço de outra funçãog()
ef()
chamag()
alguma tarefa específica. Se você passarf()
o endereço deh()
, em vez disso,f()
ligará de voltah()
.Basicamente, essa é uma maneira de parametrizar uma função: alguma parte de seu comportamento não está
f()
embutida no código , mas na função de retorno de chamada. Os chamadores podem fazer com quef()
se comportem de maneira diferente, passando diferentes funções de retorno de chamada. Um clássico éqsort()
da biblioteca padrão C, que usa seu critério de classificação como um ponteiro para uma função de comparação.Em C ++, isso geralmente é feito usando objetos de função (também chamados de functores). Esses são objetos que sobrecarregam o operador de chamada de função, portanto, você pode chamá-los como se fossem uma função. Exemplo:
A ideia por trás disso é que, ao contrário de um ponteiro de função, um objeto de função pode transportar não apenas um algoritmo, mas também dados:
Outra vantagem é que às vezes é mais fácil fazer chamadas embutidas para objetos de função do que chamadas por meio de ponteiros de função. Esta é a razão pela qual classificar em C ++ às vezes é mais rápido do que classificar em C.
fonte
Bem, eu geralmente os uso (profissionalmente) em tabelas de salto (veja também esta questão StackOverflow ).
As tabelas de salto são comumente (mas não exclusivamente) usadas em máquinas de estado finito para torná-las controladas por dados. Em vez de switch / case aninhado
você pode fazer uma matriz 2d de ponteiros de função e apenas chamar
handleEvent[state][event]
fonte
Exemplos:
fonte
std::sort
ocomp
parâmetro de como uma estratégiaO exemplo "clássico" para a utilidade dos ponteiros de função é a
qsort()
função de biblioteca C , que implementa uma Classificação Rápida. Para ser universal para toda e qualquer estrutura de dados que o usuário possa criar, são necessários alguns ponteiros vazios para dados classificáveis e um ponteiro para uma função que sabe como comparar dois elementos dessas estruturas de dados. Isso nos permite criar nossa função de escolha para o trabalho e, de fato, até permite escolher a função de comparação em tempo de execução, por exemplo, para classificar em ordem crescente ou decrescente.fonte
Concorde com todos os itens acima, mais .... Quando você carrega uma dll dinamicamente em tempo de execução, você precisa de ponteiros de função para chamar as funções.
fonte
Eu vou contra a corrente aqui.
Em C, os ponteiros de função são a única maneira de implementar a personalização, porque não há OO.
Em C ++, você pode usar ponteiros de função ou functores (objetos de função) para o mesmo resultado.
Os functores têm uma série de vantagens sobre os ponteiros de função bruta, devido à sua natureza de objeto, notavelmente:
operator()
lambda
ebind
)Eu pessoalmente prefiro functores do que ponteiros de função (apesar do código clichê), principalmente porque a sintaxe para ponteiros de função pode facilmente ficar complicada (do Tutorial de ponteiro de função ):
A única vez que vi ponteiros de função usados onde functores não podiam foi em Boost.Spirit. Eles abusaram totalmente da sintaxe para passar um número arbitrário de parâmetros como um único parâmetro de modelo.
Mas como os modelos variados e lambdas estão chegando, não tenho certeza se usaremos ponteiros de função em código C ++ puro por muito tempo.
fonte
bind
oufunction
usa ponteiros de função. É como dizer que não usamos ponteiros em C ++ porque usamos ponteiros inteligentes. De qualquer forma, estou minando.Em C, o uso clássico é a função qsort , onde o quarto parâmetro é um ponteiro para uma função a ser usada para realizar a ordenação dentro da classificação. Em C ++, haveria tendência de usar functores (objetos que se parecem com funções) para esse tipo de coisa.
fonte
Usei ponteiros de função recentemente para criar uma camada de abstração.
Tenho um programa escrito em C puro que funciona em sistemas embarcados. Ele suporta várias variantes de hardware. Dependendo do hardware que estou executando, ele precisa chamar diferentes versões de algumas funções.
No momento da inicialização, o programa descobre em qual hardware está sendo executado e preenche os ponteiros de função. Todas as rotinas de nível superior no programa apenas chamam as funções referenciadas por ponteiros. Posso adicionar suporte para novas variantes de hardware sem tocar nas rotinas de nível superior.
Eu costumava usar instruções switch / case para selecionar as versões de função adequadas, mas isso se tornou impraticável à medida que o programa crescia para oferecer suporte a mais e mais variantes de hardware. Tive de adicionar declarações de casos em todos os lugares.
Também tentei camadas de funções intermediárias para descobrir qual função usar, mas não ajudaram muito. Eu ainda precisava atualizar as instruções de caso em vários lugares sempre que adicionávamos uma nova variante. Com os ponteiros de função, só preciso alterar a função de inicialização.
fonte
Como Rich disse acima, é muito comum que os ponteiros de funções no Windows façam referência a algum endereço que armazena a função.
Quando você programa na
C language
plataforma Windows você basicamente carrega algum arquivo DLL na memória primária (usandoLoadLibrary
) e para usar as funções armazenadas na DLL você precisa criar ponteiros de funções e apontar para estes endereços (usandoGetProcAddress
).Referências:
LoadLibrary
GetProcAddress
fonte
Ponteiros de função podem ser usados em C para criar uma interface com a qual programar. Dependendo da funcionalidade específica necessária no tempo de execução, uma implementação diferente pode ser atribuída ao ponteiro de função.
fonte
Meu principal uso deles tem sido CALLBACKS: quando você precisa salvar informações sobre uma função para chamar mais tarde .
Digamos que você esteja escrevendo Bomberman. 5 segundos após a pessoa soltar a bomba, ela deve explodir (chame a
explode()
função).Agora existem 2 maneiras de fazer isso. Uma maneira é "sondar" todas as bombas na tela para ver se estão prontas para explodir no loop principal.
Outra maneira é anexar um retorno de chamada ao seu sistema de relógio. Quando uma bomba é plantada, você adiciona um retorno de chamada para fazê-la chamar bomb.explode () quando chegar a hora certa .
Aqui
callback.function()
pode haver qualquer função , porque é um ponteiro de função.fonte
Uso de ponteiro de função
Para chamar a função dinamicamente com base na entrada do usuário. Criando um mapa de string e um ponteiro de função neste caso.
Dessa forma, usamos o ponteiro de função em nossa empresa real. Você pode escrever 'n' número de função e chamá-los usando este método.
RESULTADO:
fonte
Uma perspectiva diferente, além de outras boas respostas aqui:
Em C, você só usa ponteiros de função, não funções (diretamente).
Quer dizer, você escreve funções, mas não pode manipular funções. Não há representação em tempo de execução de uma função que você possa usar. Você não pode nem chamar "uma função". Quando você escreve:
o que você está realmente dizendo é "realizar uma chamada para o
my_function
ponteiro com o argumento especificado". Você está fazendo uma chamada por meio de um ponteiro de função. Este decaimento para ponteiro de função significa que os seguintes comandos são equivalentes à chamada de função anterior:e assim por diante (obrigado @LuuVinhPhuc).
Então, você já está usando ponteiros de função como valores . Obviamente, você gostaria de ter variáveis para esses valores - e é aqui que entram todos os outros usos de metion: polimorfismo / personalização (como em qsort), callbacks, tabelas de salto etc.
Em C ++ as coisas são um pouco mais complicadas, já que temos lambdas e objetos com
operator()
e até mesmo umastd::function
classe, mas o princípio ainda é basicamente o mesmo.fonte
(&my_function)(my_arg)
,(*my_function)(my_arg)
,(**my_function)(my_arg)
,(&**my_function)(my_arg)
,(***my_function)(my_arg)
... porque funções decair para ponteiros de funçãoPara linguagens OO, para realizar chamadas polimórficas nos bastidores (isso também é válido para C até certo ponto, eu acho).
Além disso, eles são muito úteis para injetar comportamentos diferentes em outra função (foo) em tempo de execução. Isso torna a função foo função de ordem superior. Além de sua flexibilidade, isso torna o código foo mais legível, pois permite que você extraia essa lógica extra de "if-else" dele.
Ele permite muitas outras coisas úteis em Python, como geradores, fechamentos etc.
fonte
Eu uso ponteiros de função extensivamente, para emular microprocessadores que têm opcodes de 1 byte. Uma matriz de 256 ponteiros de função é a maneira natural de implementar isso.
fonte
Um uso de ponteiro de função pode ser quando não queremos modificar o código onde a função está sendo chamada (significando que a chamada pode ser condicional e sob diferentes condições, precisamos fazer diferentes tipos de processamento). Aqui, os ponteiros de função são muito úteis, uma vez que não precisamos modificar o código no local onde a função está sendo chamada. Simplesmente chamamos a função usando o ponteiro de função com os argumentos apropriados. O ponteiro de função pode ser feito para apontar para diferentes funções condicionalmente. (Isso pode ser feito em algum lugar durante a fase de inicialização). Além disso, o modelo acima é muito útil, se não estivermos em posição de modificar o código onde ele está sendo chamado (suponha que seja uma API de biblioteca que não podemos modificar). A API usa um ponteiro de função para chamar a função definida pelo usuário apropriada.
fonte
Vou tentar dar uma lista um tanto abrangente aqui:
Retornos de chamada : personalize algumas funcionalidades (biblioteca) com código fornecido pelo usuário. O exemplo principal é
qsort()
, mas também é útil para lidar com eventos (como um botão chamando um retorno de chamada quando é clicado) ou necessário para iniciar um thread (pthread_create()
).Polimorfismo : A vtable em uma classe C ++ nada mais é do que uma tabela de ponteiros de função. E um programa C também pode optar por fornecer uma vtable para alguns de seus objetos:
O construtor de
Derived
então definiria suavtable
variável de membro para um objeto global com as implementações da classe derivada dedestruct
efrobnicate
, e o código que precisava para destruir umstruct Base*
simplesmente chamariabase->vtable->destruct(base)
, o que chamaria a versão correta do destruidor, independente de qual classe derivadabase
realmente aponta para .Sem ponteiros de função, o polimorfismo precisaria ser codificado com um exército de construções de switch como
Isso se torna bastante difícil de manejar rapidamente.
Código carregado dinamicamente : quando um programa carrega um módulo na memória e tenta chamar seu código, ele deve passar por um ponteiro de função.
Todos os usos de ponteiros de função que vi se enquadram em uma dessas três classes amplas.
fonte