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.
Desafío
FácilVamos 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 tipoTimeropaco 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) yfree_timer(que recibe un puntero Timer y libera la memoria). Usa guardas de inclusión con el símboloTIMER_H.timer.c: Aquí es donde vive el secreto. Define lastruct Timerreal con dos miembros ocultos:int secondseint running(usa 1 para en ejecución, 0 para detenido). Implementa tu constructor para asignar la estructura, inicializarsecondscon el valor proporcionado y establecerrunningen 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 freedLa 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;
}
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 cuenta3Ciclo 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