🎯 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ística | Ponteiro para Função | std::function |
| Sintaxe | Complexa, requer typedef/using | Mais clara e expressiva |
| Overhead | Mí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 usar | Código de baixo nível, C, performance crítica | C++ 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...
0 Comentários