Menu
Coddy logo textTech

Definición de structs opacos

Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 18 de 61.

Ahora que entiendes el concepto de punteros opacos, veamos cómo completar el patrón. El encabezado declara que existe un tipo, pero el archivo de origen es donde defines lo que hay realmente dentro.

Aquí está la configuración completa. Primero, el encabezado con solo la declaración anticipada:

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

typedef struct Counter Counter;  // Tipo incompleto

Counter *create_counter(int start);
void free_counter(Counter *c);

#endif

Luego, el archivo fuente contiene la definición real de la estructura:

// counter.c
#include "counter.h"
#include <stdlib.h>

struct Counter {
    int value;      // ¡Oculto desde el exterior!
    int step;
};

Counter *create_counter(int start) {
    Counter *c = malloc(sizeof(Counter));
    c->value = start;
    c->step = 1;
    return c;
}

void free_counter(Counter *c) {
    free(c);
}

El punto clave: struct Counter { ... } aparece solo en el archivo .c. Cualquier archivo que incluya counter.h sabe que Counter existe pero no puede ver value o step. Intentar escribir c->value en main.c causaría un error de compilación; los miembros son verdaderamente invisibles.

Esta separación es la base de la encapsulación en C. El siguiente paso es proporcionar acceso controlado a través de funciones getter y setter.

challenge icon

Desafío

Fácil

Vamos a construir un módulo Timer que demuestre el patrón de puntero opaco (opaque pointer). Practicarás cómo ocultar la definición de la estructura dentro del archivo de origen, haciendo que sus miembros internos sean completamente invisibles para cualquier código que utilice tu módulo.

Crearás tres archivos:

  • timer.h: Esta es tu interfaz pública. Declara un tipo Timer opaco usando solo una declaración anticipada (forward declaration); ¡nada de cuerpo de estructura aquí! También declara dos funciones: create_timer (que recibe un entero para los segundos y devuelve un puntero Timer) y free_timer (que recibe un puntero Timer y libera la memoria). Usa guardas de inclusión con el símbolo TIMER_H.
  • timer.c: Aquí es donde vive el secreto. Define la struct Timer real con dos miembros ocultos: int seconds e int running (usa 1 para en ejecución, 0 para detenido). Implementa tu constructor para asignar la estructura, inicializar seconds con el valor proporcionado y establecer running en 1. Implementa el destructor para liberar la memoria.
  • main.c: Usa tu módulo Timer para crear un temporizador y confirmar que se creó correctamente. Dado que los miembros de la estructura están ocultos, no puedes acceder a ellos directamente; ¡ese es el objetivo! Simplemente imprime un mensaje confirmando la creación, luego libera el temporizador e imprime un mensaje de limpieza.

Recibirás una entrada: el número de segundos (un entero) con el que inicializar el temporizador.

En tu archivo principal, crea un temporizador con el valor de segundos proporcionado. Imprime un mensaje de creación, luego libera el temporizador e imprime una confirmación.

Imprime la salida en este formato:

Timer created
Timer freed

La idea clave aquí es que main.c puede trabajar con punteros Timer pero no tiene forma de acceder a seconds o running directamente. Intentar escribir t->seconds en main.c causaría un error de compilación porque el cuerpo de la estructura solo está definido en timer.c. Esto es verdadera encapsulación: los detalles de implementación están completamente ocultos para los usuarios de tu módulo.

Hoja de referencia

El patrón de puntero opaco separa la declaración de un tipo de su definición, ocultando los detalles de implementación.

El archivo de cabecera contiene solo una declaración anticipada:

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

typedef struct Counter Counter;  // Tipo incompleto

Counter *create_counter(int start);
void free_counter(Counter *c);

#endif

El archivo de origen contiene la definición real de la estructura:

// counter.c
#include "counter.h"
#include <stdlib.h>

struct Counter {
    int value;      // Oculto desde el exterior
    int step;
};

Counter *create_counter(int start) {
    Counter *c = malloc(sizeof(Counter));
    c->value = start;
    c->step = 1;
    return c;
}

void free_counter(Counter *c) {
    free(c);
}

La definición de la estructura aparece solo en el archivo .c. Los archivos que incluyen la cabecera saben que el tipo existe pero no pueden acceder a sus miembros directamente. Esto proporciona una verdadera encapsulación en C.

Pruébalo tú mismo

#include <stdio.h>
#include "timer.h"

int main() {
    int seconds;
    scanf("%d", &seconds);
    
    // TODO: Crear un temporizador con el valor de seconds proporcionado
    
    // TODO: Imprimir "Timer created"
    
    // TODO: Liberar el temporizador
    
    // TODO: Imprimir "Timer freed"
    
    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Object Oriented Programming