Patrón Destructor
Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 13 de 61.
Cada objeto creado con malloc debe ser liberado eventualmente. Al igual que escribimos una función create_ para manejar la asignación, escribimos una función free_ para manejar la limpieza. Este es el patrón destructor.
La idea clave es que la liberación debe ocurrir en orden inverso al de la asignación. Si su constructor asignó primero los miembros internos y luego la propia estructura (struct), su destructor debe liberar primero los miembros de la estructura y luego la estructura misma:
typedef struct {
char *name;
int age;
} Person;
void free_person(Person *p) {
if (p == NULL) return; // Comprobación de seguridad
free(p->name); // Liberar primero el miembro interno
free(p); // Luego liberar la estructura misma
}
La comprobación de NULL al inicio evita fallos si alguien pasa accidentalmente un puntero no válido. Después de liberar la cadena interna name, liberamos la estructura en sí. Si liberáramos p primero, perderíamos el acceso a p->name y causaríamos una fuga de memoria.
Usar el destructor es sencillo:
Person *alice = create_person("Alice", 30);
// ... usar alice ...
free_person(alice); // Limpiar al terminar
Este patrón garantiza que toda la memoria se libere adecuadamente. Sin él, cada objeto que crees tendría fugas de memoria hasta que el programa se quede sin ella.
Desafío
FácilVamos a construir un módulo Movie que demuestre el ciclo de vida completo de un objeto, tanto su creación como su destrucción. Practicarás la escritura de una función destructora que limpie adecuadamente toda la memoria asignada en el orden correcto.
Crearás tres archivos:
movie.h: Declara una estructuraMoviecon dos miembros:char *title(una cadena asignada dinámicamente) eint year. Declara tanto un constructorcreate_movieque reciba un título y un año, como un destructorfree_movieque reciba un puntero a Movie. Utiliza protectores de inclusión (include guards) con el símboloMOVIE_H.movie.c: Implementa ambas funciones. Tu constructor debe asignar memoria para la estructura y su cadena de título (no olvides el terminador nulo). Tu destructor debe liberar la memoria en el orden inverso a la asignación: primero la cadena internatitle, luego la estructura misma. Incluye una comprobación de NULL al inicio de tu destructor por seguridad.main.c: Crea una película, muestra su información y luego limpia adecuadamente la memoria.
Recibirás dos entradas: el título de la película (una cadena) y el año de estreno (un entero).
En tu archivo principal, utiliza create_movie para crear una nueva película, imprime su información y luego llama a free_movie para liberar toda la memoria. Después de liberarla, imprime un mensaje de confirmación.
Imprime la salida en este formato:
Movie: {title} ({year})
Memory freedPor ejemplo, con las entradas Inception y 2010, la salida sería:
Movie: Inception (2010)
Memory freedRecuerda: la clave para un destructor adecuado es liberar en orden inverso. Lo que sea que tu constructor asigne primero se libera al final, y lo que asigne al último se libera primero.
Hoja de referencia
Cada objeto creado con malloc debe ser liberado utilizando una función destructora. Los destructores siguen el patrón de liberar memoria en el orden inverso de la asignación.
Estructura básica de un destructor:
typedef struct {
char *name;
int age;
} Person;
void free_person(Person *p) {
if (p == NULL) return; // Comprobación de seguridad
free(p->name); // Liberar primero los miembros internos
free(p); // Luego liberar la estructura en sí
}
Principios clave:
- Incluir siempre una comprobación de
NULLal inicio para evitar fallos - Liberar los miembros internos antes de liberar la estructura en sí
- Si liberas la estructura primero, pierdes el acceso a sus miembros y provocas fugas de memoria
Ejemplo de uso:
Person *alice = create_person("Alice", 30);
// ... usar alice ...
free_person(alice); // Limpiar al terminar
Pruébalo tú mismo
#include <stdio.h>
#include <stdlib.h>
#include "movie.h"
int main() {
char title[100];
int year;
// Leer entrada
scanf("%[^\n]", title);
scanf("%d", &year);
// TODO: Crear una película usando create_movie
// TODO: Imprimir la información de la película en formato:
// Movie: {title} ({year})
// TODO: Liberar la película usando free_movie
// TODO: Imprimir "Memory 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