Menu
Coddy logo textTech

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

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

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

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

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

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

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

#endif

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

// 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 вызовет ошибку компилятора: members действительно невидимы.

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

challenge icon

Задание

Легко

Давайте создадим модуль Timer, демонстрирующий шаблон opaque pointer. Вы попрактикуетесь скрывать определение структуры внутри исходного файла, делая её внутренние members полностью невидимыми для любого кода, использующего ваш модуль.

Вы создадите три файла:

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

Вы получите один вход: количество seconds (целое число), которым нужно Initialize таймер.

В основном файле создайте таймер с provided значением seconds. Print сообщение о создании, затем освободите таймер и Print подтверждение.

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

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: Print "Timer created"
    
    // TODO: Освободить таймер
    
    // TODO: Вывести "Timer freed"
    
    return 0;
}
quiz iconПроверьте себя

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

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

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