Menu
CoddyTech

Определение непрозрачных структур

Часть раздела Объектно-ориентированное программирование путешествия по C на Coddy. Урок 18 из 61.

Теперь, когда ты понимаешь концепцию непрозрачных указателей, давай посмотрим, как завершить этот шаблон. Заголовочный файл объявляет о существовании типа, но именно в файле исходного кода определяется его фактическое содержимое.

Вот полная настройка. Сначала заголовочный файл только с предварительным объявлением:

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

typedef struct Counter Counter;  // Неполный тип

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

#endif

Затем исходный файл содержит фактическое определение структуры:

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

struct Counter {
    int value;      // Скрыто от внешнего кода!
    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);
}

Главное: struct Counter { ... } встречается только в файле .c. Любой файл, который включает counter.h, знает, что Counter существует, но не может видеть value или step. Попытка написать c->value в main.c приведёт к ошибке компилятора: члены структуры действительно невидимы.

Это разделение — основа инкапсуляции в C. Следующий шаг — предоставить контролируемый доступ с помощью функций получения и установки значений.

challenge icon

Задание

Легко

Создадим модуль Timer, демонстрирующий шаблон непрозрачного указателя. Ты потренируешься скрывать определение структуры в исходном файле, делая её внутренние поля совершенно недоступными для кода, использующего твой модуль.

Ты создашь три файла:

  • timer.h: это публичный интерфейс. Объяви непрозрачный тип Timer, используя только предварительное объявление: здесь не должно быть тела структуры! Также объяви две функции: create_timer (принимает целое число секунд и возвращает указатель на Timer) и free_timer (принимает указатель на Timer и освобождает память). Используй защиту от повторного включения с символом TIMER_H.
  • timer.c: здесь хранится секрет. Определи настоящую struct Timer с двумя скрытыми полями: int seconds и int running (используй 1 для работающего состояния и 0 для остановленного). Реализуй конструктор, чтобы выделить память под структуру, инициализировать seconds переданным значением и установить running в 1. Реализуй деструктор, чтобы освободить память.
  • main.c: используй модуль Timer, чтобы создать таймер и убедиться, что он успешно создан. Поскольку поля структуры скрыты, обращаться к ним напрямую нельзя. В этом и заключается суть! Просто выведи сообщение, подтверждающее создание, затем освободи память таймера и выведи сообщение об очистке.

На вход будет подано одно значение: количество секунд (целое число), которым нужно инициализировать таймер.

В главном файле создай таймер, используя переданное значение секунд. Выведи сообщение о создании, затем освободи память таймера и выведи подтверждение.

Выведи результат в следующем формате:

Timer created
Timer freed

Главный вывод здесь в том, что main.c может работать с указателями на Timer, но не имеет возможности обращаться напрямую к seconds или running. Попытка написать t->seconds в main.c приведёт к ошибке компилятора, потому что тело структуры определено только в timer.c. Это и есть настоящая инкапсуляция: детали реализации полностью скрыты от пользователей модуля.

Попробуйте сами

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

int main() {
    int seconds;
    scanf("%d", &seconds);
    
    // TODO: Создайте таймер с заданным значением секунд
    
    // TODO: Выведите "Timer created"
    
    // TODO: Освободите память, выделенную для таймера
    
    // TODO: Выведите "Timer freed"
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C