Определение непрозрачных структур
Часть раздела Объектно-ориентированное программирование путешествия по 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.
Задание
ЛегкоДавайте создадим модуль 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 структуру, Initializesecondsпредоставленным значением и установить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;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация кругаРеализация прямоугольникаПолиморфное использованиеКонтейнер фигурПотренируйтесь самостоятельно: Онлайн-компилятор C