🎯 Ponteiros para Funções · Callbacks e Tabelas · C/C++

🎯 Ponteiros para Funções

"Um ponteiro para função é uma variável que armazena o endereço de uma função. Ele permite que funções sejam passadas como argumentos, armazenadas em arrays e chamadas dinamicamente."

Assim como ponteiros podem armazenar endereços de variáveis, eles também podem armazenar endereços de funções. Isso permite um nível avançado de flexibilidade: callbacks, tabelas de despacho, máquinas de estado e personalização de comportamento em tempo de execução.

📌 Declaração de Ponteiros para Funções

A sintaxe pode parecer intimidante, mas segue um padrão lógico.

// Sintaxe: tipo_retorno (*nome_do_ponteiro)(tipo_param1, tipo_param2, ...); // Exemplo: ponteiro para função que recebe dois ints e retorna int int (*operacao)(int, int); // Ponteiro para função que recebe const char* e retorna void void (*logger)(const char*); // Ponteiro para função sem parâmetros que retorna bool bool (*validador)();
⚠️ Não confunda! Os parênteses ao redor de *nome são essenciais.
int (*pf)(int); // Ponteiro para função que recebe int e retorna int int* f(int); // Função que recebe int e retorna ponteiro para int (diferente!)

🔗 Atribuindo e Chamando

O nome de uma função (sem parênteses) é um ponteiro para ela. Atribuir e chamar é direto.

int somar(int a, int b) { return a + b; } int subtrair(int a, int b) { return a - b; } int main() { int (*op)(int, int); // Declara o ponteiro op = somar; // Atribui o endereço da função somar cout << op(5, 3) << endl; // Chama através do ponteiro: 8 op = subtrair; // Atribui o endereço da função subtrair cout << op(5, 3) << endl; // Chama através do ponteiro: 2 // Também pode usar a sintaxe de desreferência explícita cout << (*op)(10, 4) << endl; // 6 }

📤 Passando Ponteiros para Funções como Argumentos

O uso mais comum de ponteiros para funções é como callbacks — funções que são passadas para outras funções para serem chamadas posteriormente.

// Função que aplica uma operação a cada elemento do array void mapear(int* arr, int tam, int (*transformar)(int)) { for (int i = 0; i < tam; i++) { arr[i] = transformar(arr[i]); } } int dobrar(int x) { return x * 2; } int quadrado(int x) { return x * x; } int main() { int numeros[5] = {1, 2, 3, 4, 5}; mapear(numeros, 5, dobrar); // numeros agora: 2, 4, 6, 8, 10 mapear(numeros, 5, quadrado); // numeros agora: 4, 16, 36, 64, 100 }

📊 Arrays de Ponteiros para Funções

Útil para implementar tabelas de despacho, máquinas de estado ou menus.

void opcao1() { cout << "Executando opção 1" << endl; } void opcao2() { cout << "Executando opção 2" << endl; } void opcao3() { cout << "Executando opção 3" << endl; } int main() { // Array de ponteiros para funções void (*menu[3])() = {opcao1, opcao2, opcao3}; int escolha; cout << "Escolha (1-3): "; cin >> escolha; if (escolha >= 1 && escolha <= 3) { menu[escolha - 1](); // Chama a função correspondente } }

🧹 Simplificando com typedef e using

Declarações de ponteiros para funções podem ficar complexas. typedef (C) ou using (C++) simplificam.

// Usando typedef (estilo C) typedef int (*OperacaoBinaria)(int, int); OperacaoBinaria op = somar; cout << op(5, 3) << endl; // Usando using (C++11) — mais legível using Operacao = int (*)(int, int); Operacao op2 = subtrair; cout << op2(5, 3) << endl; // Também funciona com std::function (C++11) — ainda mais flexível #include <functional> std::function<int(int, int)> op3 = somar; cout << op3(5, 3) << endl;

🔄 Exemplo Prático: Função de Ordenação Genérica

A função qsort da biblioteca padrão C usa ponteiro para função como comparador.

#include <cstdlib> int compararInt(const void* a, const void* b) { int ia = *static_cast<const int*>(a); int ib = *static_cast<const int*>(b); return ia - ib; // Ordem crescente } int main() { int arr[5] = {5, 2, 8, 1, 9}; qsort(arr, 5, sizeof(int), compararInt); // arr agora: 1, 2, 5, 8, 9 }

📊 Ponteiros para Funções vs. std::function

CaracterísticaPonteiro para Funçãostd::function
SintaxeComplexa, requer typedef/usingMais clara e expressiva
OverheadMínimo (indireção simples)Maior (type erasure, possível alocação)
Pode armazenar lambdas?Apenas lambdas sem captura✅ Sim (qualquer callable)
Pode armazenar functors?❌ Não✅ Sim
Quando usarCódigo de baixo nível, C, performance críticaC++ moderno, flexibilidade

🎯 Exemplo Prático: Callback de Evento

// Sistema de eventos simples usando ponteiro para função using Callback = void (*)(const std::string&); class Botao { Callback aoClicar = nullptr; public: void setOnClick(Callback cb) { aoClicar = cb; } void clicar() { if (aoClicar) aoClicar("Botão foi clicado!"); } }; void meuCallback(const std::string& msg) { cout << "Evento: " << msg << endl; } int main() { Botao btn; btn.setOnClick(meuCallback); btn.clicar(); // Evento: Botão foi clicado! }

💡 Boas Práticas

  • Use typedef ou using para simplificar declarações complexas.
  • Em C++ moderno, prefira std::function para maior flexibilidade.
  • Verifique se o ponteiro não é nulo antes de chamá-lo.
  • Documente claramente o contrato da função esperada (parâmetros, retorno, efeitos colaterais).
  • Considere usar lambdas (C++11) em vez de funções separadas para callbacks simples.

🔗 Conclusão

Ponteiros para funções são um recurso poderoso que permite implementar callbacks, tabelas de despacho e comportamento dinâmico. Embora a sintaxe possa ser intimidadora, dominá-la abre portas para padrões de design avançados e maior flexibilidade no código. Em C++ moderno, std::function e lambdas oferecem alternativas mais expressivas, mas compreender os ponteiros para funções tradicionais ainda é essencial para código de baixo nível e interoperabilidade com C.

⏭️ Próximo: Funções in-line...

Postar um comentário

0 Comentários