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

Herencia en C con composición: ejercicio resuelto

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.

Enunciado

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.

Solución en C

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#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;
}

Resultado esperado

1
2
Perro Toby: guau (energía=80)
Gato Misu: miau (vidas=9)

Errores frecuentes

  • 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.

Aplicación práctica

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.

Siguiente ejercicio recomendado

Práctica guiada y siguiente paso

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

FAQ

¿La herencia en C existe de forma nativa?

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

¿Cuándo merece la pena usar punteros a función?

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

¿Este patrón sustituye por completo a la POO?

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