Simulando métodos
Parte de la sección Programación Orientada a Objetos del Journey de C de Coddy. Lección 39 de 61.
Has visto cómo colocar un puntero a función dentro de una estructura. El verdadero poder aparece cuando asignas funciones diferentes a distintas instancias del mismo tipo de estructura. Así simulamos métodos con un comportamiento variable.
Considera una estructura que representa una acción con lógica personalizable:
typedef void (*ActionFunc)(void);
typedef struct {
const char* name;
ActionFunc execute;
} Action;Al crear instancias, conectas diferentes funciones a cada una:
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(); // Salida: Hello!
farewell.execute(); // Salida: Goodbye!
return 0;
}Tanto greet como farewell son del mismo tipo (Action), pero llamar a execute en cada uno produce resultados completamente diferentes. La estructura no solo contiene datos. Contiene un comportamiento que varía según la instancia.
Este patrón refleja cómo funcionan los objetos en otros lenguajes: cada objeto puede tener su propia implementación de un método, aunque compartan la misma interfaz.
Desafío
FácilConstruyamos un sistema Speaker que demuestre cómo distintas instancias del mismo tipo de estructura pueden mostrar comportamientos completamente diferentes según la función que se conecte a ellas.
Crearás dos archivos para organizar tu código:
speaker.h: Define un tipo de puntero a función llamadoSpeakFuncque no acepta parámetros y no devuelve nada. Después, define una estructuraSpeakerque contenga unname(unconst char*) y un puntero a funciónspeakde tipoSpeakFunc.main.c: Incluye tu archivo de cabecera e implementa tres funciones de habla diferentes:bark: imprimeWoof!meow: imprimeMeow!chirp: imprimeChirp!
Speaker, conecta cada una a una función diferente según la entrada y llama a sus métodosspeakpara observar el comportamiento polimórfico en acción.
Tu programa recibirá dos entradas: el tipo del primer hablante (1 para bark, 2 para meow, 3 para chirp) y el tipo del segundo hablante (también 1, 2 o 3).
Crea dos instancias de Speaker. Puedes ponerles el nombre que quieras (como "Perro", "Gato", "Pájaro" o simplemente "Speaker1", "Speaker2"). Conecta cada una a la función apropiada según su código de entrada y después llama a speak en cada instancia, en orden.
Salida de ejemplo cuando el primer tipo es 1 y el segundo tipo es 2:
Woof!
Meow!Salida de ejemplo cuando el primer tipo es 3 y el segundo tipo es 3:
Chirp!
Chirp!La idea clave aquí es que ambos hablantes son del mismo tipo Speaker y, aun así, producen salidas diferentes porque cada uno tiene una función distinta asignada a su miembro speak. Recuerda usar protectores de inclusión en tu archivo de cabecera.
Pruébalo tú mismo
#include <stdio.h>
#include "speaker.h"
// TODO: Implementa la función bark que imprime "Woof!"
// TODO: Implementa la función meow que imprime "Meow!"
// TODO: Implementa la función chirp que imprime "Chirp!"
int main() {
int type1, type2;
scanf("%d", &type1);
scanf("%d", &type2);
// TODO: Crea dos instancias de Speaker
// TODO: Conecta la función speak del primer speaker basándote en type1
// (1 = bark, 2 = meow, 3 = chirp)
// TODO: Conecta la función speak del segundo speaker basándote en type2
// (1 = bark, 2 = meow, 3 = chirp)
// TODO: Llama a speak en cada speaker en orden
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de programación modular
Archivos de cabeceraGuardas de inclusiónArchivos fuenteFunciones estáticasRepaso: Calculadora modular4Encapsulamiento
Concepto de punteros opacosDefinición de structs opacosGetters y SettersValidación en settersResumen: Caja secreta7Punteros a funciones
Declaración de punteros a funcionesLlamada a punteros a funcionesTypedef para punteros a funcionesPasar funciones como argumentosRepaso: Dispatch de calculadora10Contenedores genéricos
Repaso de punteros voidWrapper genéricoSwap genéricoComparación genéricaRepaso: Array genérico2Objetos y métodos
Structs como objetosEl puntero 'Self'Const CorrectnessPuntero vs. ValorMétodos auxiliaresResumen: Point Manager5Proyecto: Cuenta bancaria simple
Configuración del proyectoImplementación de la cuenta8Polimorfismo
Punteros a funciones en estructurasSimulando métodosEl concepto de interfazImplementando interfacesIteración polimórficaResumen: Greeter3Ciclo de vida de los objetos
Patrón ConstructorPatrón DestructorInicialización en StackCopia profundaResumen: String Wrapper6Herencia mediante composición
Incrustación de StructsLa regla del primer miembroAcceso a miembros padreUpcastingResumen: Jerarquía de formas9Proyecto: Dibujador de figuras
Descripción general del proyectoImplementación del círculoImplementación del rectánguloUso polimórficoContenedor de figurasPractica por tu cuenta: Compilador de C online