Punteros a funciones en estructuras
Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 38 de 61.
Hasta ahora, hemos utilizado punteros a funciones como variables independientes o los hemos pasado como argumentos. El siguiente paso hacia el polimorfismo es incrustar un puntero a función directamente dentro de un struct, dándole al struct su propio comportamiento.
Cuando un struct contiene un puntero a función, cada instancia puede contener una función diferente. Esto significa que dos objetos del mismo tipo pueden comportarse de manera diferente cuando llamas a su puntero a función:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // Puntero a función como miembro
} Printer;La estructura Printer ahora tiene dos miembros: datos (name) y comportamiento (print). Para usarla, asignas una función al puntero durante la inicialización:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // Salida: !!! Hello !!!
return 0;
}Observa cómo llamamos a la función a través del struct: p.print("Hello"). Esto se parece notablemente a llamar a un método en un objeto en otros lenguajes. El struct contiene tanto sus datos como la función que opera sobre ellos.
Este patrón es la base del polimorfismo en C: diferentes instancias pueden tener diferentes funciones asignadas, produciendo diferentes comportamientos a partir del mismo tipo struct.
Desafío
FácilVamos a construir un sistema Notifier que demuestre cómo los structs pueden llevar su propio comportamiento a través de punteros a funciones.
Crearás dos archivos para organizar tu código:
notifier.h: Define un tipo de puntero a función llamadoNotifyFuncque reciba un parámetroconst char*y no devuelva nada. Luego, define un structNotifierque contenga unname(unconst char*) y un puntero a funciónnotifyde tipoNotifyFunc.main.c: Incluye tu cabecera e implementa dos funciones de notificación:alert_notify— imprime el mensaje en el formato:[ALERT] messageinfo_notify— imprime el mensaje en el formato:[INFO] message
Notifier, conecta la función apropiada basada en la entrada y llama a la notificación a través del puntero a función del struct.
Tu programa recibirá dos entradas: un tipo de notificación (1 para alert, 2 para info) y un mensaje para mostrar.
Basándote en el tipo, asigna la función correspondiente al miembro notify de tu notifier, luego llámala a través del struct para mostrar el mensaje.
Ejemplo de salida cuando el tipo es 1 y el mensaje es "System starting":
[ALERT] System startingEjemplo de salida cuando el tipo es 2 y el mensaje es "All systems normal":
[INFO] All systems normalRecuerda usar guardas de inclusión en tu archivo de cabecera y llamar a la función a través del miembro del struct: n.notify(message)
Hoja de referencia
Los punteros a funciones pueden incrustarse directamente dentro de las estructuras, otorgando a cada instancia su propio comportamiento. Esta es una base para el polimorfismo en C.
Definición de una estructura con un puntero a función
Primero, defina un tipo de puntero a función y luego inclúyalo como miembro de la estructura:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // Function pointer as a member
} Printer;Uso de la estructura
Asigne una función al puntero durante la inicialización y luego llámela a través de la estructura:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // Output: !!! Hello !!!
return 0;
}La sintaxis p.print("Hello") llama a la función a través de la estructura, de manera similar a llamar a un método en un objeto en otros lenguajes.
Comportamiento polimórfico
Diferentes instancias del mismo tipo de estructura pueden tener asignadas funciones diferentes, produciendo comportamientos distintos a partir del mismo tipo de estructura.
Pruébalo tú mismo
#include <stdio.h>
#include "notifier.h"
// TODO: Implementar la función alert_notify
// Debería imprimir: [ALERT] message
// TODO: Implementar la función info_notify
// Debería imprimir: [INFO] message
int main() {
int type;
char message[256];
// Leer entrada
scanf("%d", &type);
getchar(); // consumir nueva línea
fgets(message, sizeof(message), stdin);
// Eliminar el salto de línea final del mensaje si está presente
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: Crear una instancia de Notifier
// TODO: Basado en el tipo (1 para alerta, 2 para información),
// asignar la función apropiada al miembro notify del notifier
// TODO: Llamar a la notificación a través del puntero de función de la estructura
// Usar: n.notify(message)
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
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írculo