Herencia en C: ejercicio resuelto con composición y punteros

  3 minutos

Si buscas herencia en C, el enfoque práctico es simularla con composición y punteros a función, porque C no incluye herencia nativa.

Este patrón te permite modelar jerarquías simples y comportamiento dinámico sin salir de C puro.

Modela una jerarquía básica:

  1. tipo base Animal con nombre y función hablar,
  2. tipo Perro y tipo Gato que “extienden” a Animal por composición,
  3. recorrido de un array de Animal* para ejecutar comportamiento polimórfico.
#include <stdio.h>

typedef struct Animal Animal;
typedef void (*HablarFn)(const Animal *);

struct Animal {
    const char *nombre;
    HablarFn hablar;
};

typedef struct {
    Animal base;
    int energia;
} Perro;

typedef struct {
    Animal base;
    int vidas;
} Gato;

void perro_hablar(const Animal *a) {
    const Perro *p = (const Perro *)a;
    printf("Perro %s: guau (energía=%d)\n", p->base.nombre, p->energia);
}

void gato_hablar(const Animal *a) {
    const Gato *g = (const Gato *)a;
    printf("Gato %s: miau (vidas=%d)\n", g->base.nombre, g->vidas);
}

Perro perro_crear(const char *nombre, int energia) {
    Perro p;
    p.base.nombre = nombre;
    p.base.hablar = perro_hablar;
    p.energia = energia;
    return p;
}

Gato gato_crear(const char *nombre, int vidas) {
    Gato g;
    g.base.nombre = nombre;
    g.base.hablar = gato_hablar;
    g.vidas = vidas;
    return g;
}

int main(void) {
    Perro p = perro_crear("Toby", 80);
    Gato g = gato_crear("Misu", 9);

    Animal *grupo[] = {(Animal *)&p, (Animal *)&g};
    int n = (int)(sizeof(grupo) / sizeof(grupo[0]));

    for (int i = 0; i < n; i++) {
        grupo[i]->hablar(grupo[i]);
    }

    return 0;
}
Perro Toby: guau (energía=80)
Gato Misu: miau (vidas=9)
  • Copiar solo el tipo base en vez de trabajar con punteros al tipo compuesto.
  • Olvidar inicializar el puntero a función y provocar fallo en tiempo de ejecución.
  • Hacer cast de tipos no compatibles.
  • Intentar replicar herencia compleja de POO clásica en C sin necesidad.

Este patrón se usa para:

  • diseñar motores y librerías con callbacks,
  • modelar plugins y controladores con interfaz común,
  • reducir acoplamiento entre módulos.

Es una habilidad útil para C de sistemas y código mantenible.

Si quieres una ruta completa con progresión real de dificultad:

No. En C se simula con composición, punteros y funciones para compartir interfaz y comportamiento.

Cuando necesitas comportamiento intercambiable en tiempo de ejecución, por ejemplo estrategias, callbacks o controladores.

No. C no ofrece todas las abstracciones de POO, pero este enfoque cubre muchos casos prácticos de diseño modular.