Ponteiros de Função em Structs
Parte da seção Object Oriented Programming do Journey de C da Coddy — lição 38 de 61.
Até agora, usamos ponteiros de função como variáveis independentes ou os passamos como argumentos. O próximo passo em direção ao polimorfismo é incorporar um ponteiro de função diretamente dentro de uma struct — dando à struct seu próprio comportamento.
Quando uma struct contém um ponteiro de função, cada instância pode conter uma função diferente. Isso significa que dois objetos do mesmo tipo podem se comportar de forma diferente quando você chama seu ponteiro de função:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // Ponteiro de função como um membro
} Printer;A struct Printer agora possui dois membros: dados (name) e comportamento (print). Para utilizá-la, você atribui uma função ao ponteiro durante a inicialização:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // Saída: !!! Hello !!!
return 0;
}Observe como chamamos a função através da struct: p.print("Hello"). Isso se parece muito com a chamada de um método em um objeto em outras linguagens. A struct carrega tanto seus dados quanto a função que opera sobre eles.
Este padrão é a base do polimorfismo em C — diferentes instâncias podem ter diferentes funções atribuídas, produzindo comportamentos diferentes a partir do mesmo tipo de struct.
Desafio
FácilVamos construir um sistema Notifier que demonstra como structs podem carregar seu próprio comportamento através de ponteiros de função.
Você criará dois arquivos para organizar seu código:
notifier.h: Defina um tipo de ponteiro de função chamadoNotifyFuncque recebe um parâmetroconst char*e não retorna nada. Em seguida, defina uma structNotifierque contém umname(umconst char*) e um ponteiro de funçãonotifydo tipoNotifyFunc.main.c: Inclua seu cabeçalho e implemente duas funções de notificação:alert_notify— imprime a mensagem no formato:[ALERT] messageinfo_notify— imprime a mensagem no formato:[INFO] message
Notifier, conecte a função apropriada com base na entrada e chame a notificação através do ponteiro de função da struct.
Seu programa receberá duas entradas: um tipo de notificação (1 para alerta, 2 para informação) e uma mensagem para exibir.
Com base no tipo, atribua a função correspondente ao membro notify do seu notifier e, em seguida, chame-a através da struct para exibir a mensagem.
Exemplo de saída quando o tipo é 1 e a mensagem é "System starting":
[ALERT] System startingExemplo de saída quando o tipo é 2 e a mensagem é "All systems normal":
[INFO] All systems normalLembre-se de usar guards de inclusão em seu arquivo de cabeçalho e chamar a função através do membro da struct: n.notify(message)
Folha de consulta
Ponteiros de função podem ser incorporados diretamente dentro de structs, dando a cada instância seu próprio comportamento. Esta é uma base para o polimorfismo em C.
Definindo uma Struct com um Ponteiro de Função
Primeiro, defina um tipo de ponteiro de função e, em seguida, inclua-o como um membro da struct:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // Ponteiro de função como um membro
} Printer;Usando a Struct
Atribua uma função ao ponteiro durante a inicialização e, em seguida, chame-a através da struct:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // Saída: !!! Hello !!!
return 0;
}A sintaxe p.print("Hello") chama a função através da struct, semelhante à chamada de um método em um objeto em outras linguagens.
Comportamento Polimórfico
Diferentes instâncias do mesmo tipo de struct podem ter diferentes funções atribuídas, produzindo comportamentos diferentes a partir do mesmo tipo de struct.
Experimente você mesmo
#include <stdio.h>
#include "notifier.h"
// TODO: Implementar a função alert_notify
// Deve imprimir: [ALERT] message
// TODO: Implementar a função info_notify
// Deve imprimir: [INFO] message
int main() {
int type;
char message[256];
// Ler a entrada
scanf("%d", &type);
getchar(); // consumir a nova linha
fgets(message, sizeof(message), stdin);
// Remover a nova linha ao final de message, se presente
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: Criar uma instância de Notifier
// TODO: Com base no type (1 para alert, 2 para info),
// atribuir a função apropriada ao membro notify do notifier
// TODO: Chamar a notificação através do ponteiro de função da struct
// Use: n.notify(message)
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Object Oriented Programming
1Fundamentos da Programação Modular
Arquivos de CabeçalhoInclude GuardsArquivos-FonteFunções EstáticasRecapitulação: Calculadora Modular4Encapsulamento
Conceito de Ponteiros OpacosDefinindo Structs OpacasGetters e SettersValidação em SettersRecapitulação: Caixa Secreta2Objetos e Métodos
Structs como ObjetosO Ponteiro 'Self'Const CorrectnessPonteiro vs ValorMétodos AuxiliaresRecapitulação: Point Manager5Projeto: Conta Bancária Simples
Configuração do ProjetoImplementação da Conta8Polimorfismo
Ponteiros de Função em StructsSimulando MétodosO Conceito de InterfaceImplementando InterfacesIteração PolimórficaRecapitulação: Greeter3Ciclo de Vida de Objetos
Padrão de ConstrutorPadrão de DestrutorInicialização na StackCópia ProfundaRecapitulação: String Wrapper6Herança via Composição
Incorporação de StructsA Regra do Primeiro MembroAcessando Membros PaiUpcastingRecapitulação: Hierarquia de Formas9Projeto: Desenhador de Formas
Visão Geral do ProjetoImplementação do Círculo