Паттерн «Деструктор»
Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 13 из 61.
Каждый объект, созданный с помощью malloc, должен быть в конечном итоге освобожден. Так же, как мы написали функцию create_ для управления выделением памяти, мы пишем функцию free_ для управления очисткой. Это паттерн «деструктор».
Ключевая идея заключается в том, что освобождение памяти должно происходить в порядке, обратном порядку выделения. Если ваш конструктор сначала выделил память для внутренних членов, а затем для самой структуры, то ваш деструктор должен сначала освободить члены структуры, а затем саму структуру:
typedef struct {
char *name;
int age;
} Person;
void free_person(Person *p) {
if (p == NULL) return; // Проверка на NULL
free(p->name); // Сначала освобождаем внутренний член
free(p); // Затем освобождаем саму структуру
}
Проверка на NULL в начале предотвращает сбои, если кто-то случайно передаст неверный указатель. После освобождения внутренней строки name мы освобождаем саму структуру. Если бы мы сначала освободили p, мы бы потеряли доступ к p->name и вызвали утечку памяти.
Использование деструктора довольно просто:
Person *alice = create_person("Alice", 30);
// ... используем alice ...
free_person(alice); // Освобождаем память после использования
Этот паттерн гарантирует правильное освобождение всей памяти. Без него каждый созданный вами объект будет вызывать утечку памяти, пока ресурсы программы не будут исчерпаны.
Задание
ЛегкоДавайте создадим модуль Movie, который демонстрирует полный жизненный цикл объекта — как создание, так и уничтожение. Вы попрактикуетесь в написании функции-деструктора, которая правильно очищает всю выделенную память в нужном порядке.
Вы создадите три файла:
movie.h: Объявите структуруMovieс двумя членами:char *title(динамически выделенная строка) иint year. Объявите конструкторcreate_movie, который принимает название и год, и деструкторfree_movie, который принимает указатель на Movie. Используйте include guards с символомMOVIE_H.movie.c: Реализуйте обе функции. Ваш конструктор должен выделять память для структуры и её строки заголовка (не забудьте про нулевой терминатор). Ваш деструктор должен освобождать память в порядке, обратном выделению — сначала внутреннюю строкуtitle, затем саму структуру. Для безопасности добавьте проверку на NULL в начале деструктора.main.c: Создайте фильм, выведите информацию о нем, а затем правильно очистите память.
Вы получите два входных значения: название фильма (строка) и год выпуска (целое число).
В вашем основном файле используйте create_movie для создания нового фильма, выведите информацию о нем, а затем вызовите free_movie, чтобы освободить всю память. После освобождения выведите подтверждающее сообщение.
Выведите результат в следующем формате:
Movie: {title} ({year})
Memory freedНапример, при входных данных Inception и 2010 результат будет следующим:
Movie: Inception (2010)
Memory freedПомните: ключ к правильному деструктору — освобождение в обратном порядке. То, что ваш конструктор выделяет первым, освобождается последним, а то, что он выделяет последним, освобождается первым.
Шпаргалка
Каждый объект, созданный с помощью malloc, должен быть освобожден с использованием функции-деструктора. Деструкторы следуют принципу освобождения памяти в обратном порядке выделения.
Базовая структура деструктора:
typedef struct {
char *name;
int age;
} Person;
void free_person(Person *p) {
if (p == NULL) return; // Проверка на NULL
free(p->name); // Сначала освобождаем внутренние поля
free(p); // Затем освобождаем саму структуру
}
Ключевые принципы:
- Всегда включайте проверку на
NULLв начале, чтобы предотвратить сбои - Освобождайте внутренние поля перед освобождением самой структуры
- Если вы сначала освободите структуру, вы потеряете доступ к её полям, что приведет к утечкам памяти
Пример использования:
Person *alice = create_person("Alice", 30);
// ... используем alice ...
free_person(alice); // Очистка по завершении
Попробуйте сами
#include <stdio.h>
#include <stdlib.h>
#include "movie.h"
int main() {
char title[100];
int year;
// Чтение входных данных
scanf("%[^\n]", title);
scanf("%d", &year);
// TODO: Создайте фильм с помощью create_movie
// TODO: Выведите информацию о фильме в формате:
// Movie: {title} ({year})
// TODO: Освободите память фильма с помощью free_movie
// TODO: Выведите "Memory freed"
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация круга