Simulando Métodos
Parte da seção Object Oriented Programming do Journey de C da Coddy — lição 39 de 61.
Você viu como colocar um ponteiro de função dentro de uma struct. O verdadeiro poder surge quando você atribui funções diferentes a instâncias diferentes do mesmo tipo de struct — é assim que simulamos métodos com comportamentos variados.
Considere uma struct que representa uma ação com lógica customizável:
typedef void (*ActionFunc)(void);
typedef struct {
const char* name;
ActionFunc execute;
} Action;Ao criar instâncias, você conecta funções diferentes a cada uma delas:
void say_hello(void) {
printf("Hello!\n");
}
void say_goodbye(void) {
printf("Goodbye!\n");
}
int main() {
Action greet = { "Greet", say_hello };
Action farewell = { "Farewell", say_goodbye };
greet.execute(); // Saída: Hello!
farewell.execute(); // Saída: Goodbye!
return 0;
}Tanto greet quanto farewell são do mesmo tipo (Action), mas chamar execute em cada um produz resultados completamente diferentes. A struct não apenas armazena dados — ela armazena comportamento que varia por instância.
Este padrão reflete como os objetos funcionam em outras linguagens: cada objeto pode ter sua própria implementação de um método, embora compartilhem a mesma interface.
Desafio
FácilVamos construir um sistema Speaker que demonstra como diferentes instâncias do mesmo tipo de struct podem exibir comportamentos completamente diferentes com base em qual função está conectada a elas.
Você criará dois arquivos para organizar seu código:
speaker.h: Defina um tipo de ponteiro de função chamadoSpeakFuncque não recebe parâmetros e não retorna nada. Em seguida, defina uma structSpeakercontendo umname(umconst char*) e um ponteiro de funçãospeakdo tipoSpeakFunc.main.c: Inclua seu cabeçalho e implemente três funções de fala diferentes:bark— imprimeWoof!meow— imprimeMeow!chirp— imprimeChirp!
Speaker, cada uma conectada a uma função diferente com base na entrada, e chame seus métodosspeakpara ver o comportamento polimórfico em ação.
Seu programa receberá duas entradas: o primeiro tipo de alto-falante (1 para bark, 2 para meow, 3 para chirp) e o segundo tipo de alto-falante (também 1, 2 ou 3).
Crie duas instâncias de Speaker — você pode nomeá-las como desejar (como "Dog", "Cat", "Bird", ou simplesmente "Speaker1", "Speaker2"). Conecte cada uma à função apropriada com base em seu código de entrada e, em seguida, chame speak em cada instância em ordem.
Exemplo de saída quando o primeiro tipo é 1 e o segundo tipo é 2:
Woof!
Meow!Exemplo de saída quando o primeiro tipo é 3 e o segundo tipo é 3:
Chirp!
Chirp!A ideia principal aqui é que ambos os alto-falantes são do mesmo tipo Speaker, mas produzem saídas diferentes porque cada um tem uma função diferente atribuída ao seu membro speak. Lembre-se de usar guards de inclusão em seu arquivo de cabeçalho.
Folha de consulta
Diferentes instâncias do mesmo tipo de struct podem conter diferentes ponteiros de função, permitindo um comportamento polimórfico.
Defina um tipo de ponteiro de função e uma struct que o utilize:
typedef void (*ActionFunc)(void);
typedef struct {
const char* name;
ActionFunc execute;
} Action;Crie instâncias com diferentes funções atribuídas:
void say_hello(void) {
printf("Hello!\n");
}
void say_goodbye(void) {
printf("Goodbye!\n");
}
int main() {
Action greet = { "Greet", say_hello };
Action farewell = { "Farewell", say_goodbye };
greet.execute(); // Output: Hello!
farewell.execute(); // Output: Goodbye!
return 0;
}Ambas as instâncias compartilham o mesmo tipo, mas exibem comportamentos diferentes quando seus ponteiros de função são chamados.
Experimente você mesmo
#include <stdio.h>
#include "speaker.h"
// TODO: Implementar a função bark que imprime "Woof!"
// TODO: Implementar a função meow que imprime "Meow!"
// TODO: Implementar a função chirp que imprime "Chirp!"
int main() {
int type1, type2;
scanf("%d", &type1);
scanf("%d", &type2);
// TODO: Criar duas instâncias de Speaker
// TODO: Conectar a função speak do primeiro alto-falante com base em type1
// (1 = bark, 2 = meow, 3 = chirp)
// TODO: Conectar a função speak do segundo alto-falante com base em type2
// (1 = bark, 2 = meow, 3 = chirp)
// TODO: Chamar speak em cada alto-falante em ordem
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