Геттеры и сеттеры
Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 19 из 61.
При использовании непрозрачных указателей члены структуры скрыты от внешнего кода. Но если внешний код не может получить прямой доступ к данным, как нам их читать или изменять? Ответ заключается в функциях-геттерах и сеттерах — публичных интерфейсах, которые обеспечивают контролируемый доступ к приватным данным.
Геттер возвращает значение скрытого члена:
// counter.h
int counter_get_value(const Counter *c);
// counter.c
int counter_get_value(const Counter *c) {
return c->value;
}
Сеттер изменяет скрытый член:
// counter.h
void counter_set_value(Counter *c, int new_value);
// counter.c
void counter_set_value(Counter *c, int new_value) {
c->value = new_value;
}
Обратите внимание на соглашение об именовании: добавляйте к функциям префикс с именем типа (counter_), чтобы избежать конфликтов и сделать API понятным. Геттеры используют const указатели, так как они не изменяют объект.
Теперь внешний код может безопасно взаимодействовать со скрытыми данными:
// main.c
Counter *c = create_counter(10);
printf("%d\n", counter_get_value(c)); // 10
counter_set_value(c, 25);
printf("%d\n", counter_get_value(c)); // 25
Этот паттерн дает вам полный контроль над тем, как осуществляется доступ к данным — это основа, на которой мы будем строить в следующем уроке, когда добавим логику валидации в сеттеры.
Задание
ЛегкоДавайте создадим модуль Score, который использует паттерн непрозрачного указателя (opaque pointer) с функциями геттерами и сеттерами. Вы создадите скрытый трекер счета, где внешний код сможет взаимодействовать с данными только через предоставленный вами публичный интерфейс.
Вы создадите три файла:
score.h: Ваш публичный интерфейс. Объявите непрозрачный типScore, используя опережающее объявление (без тела структуры!). Объявите следующие функции:create_score— принимает целое число для начальных очков и возвращает указатель на Scorefree_score— принимает указатель на Score и освобождает памятьscore_get_points— геттер, который принимаетconst Score *и возвращает текущие очкиscore_set_points— сеттер, который принимаетScore *и целое число, обновляя значение очков
SCORE_H.score.c: Определите самуstruct Scoreс единственным скрытым членом:int points. Реализуйте все четыре функции. Ваш геттер должен возвращать скрытое значение, а сеттер — обновлять его. Помните, что геттеры используют указателиconst, так как они не изменяют объект.main.c: Продемонстрируйте работу вашего модуля, создав счет, прочитав его значение, изменив его через сеттер и прочитав снова.
Вы получите два входных значения: начальные очки (целое число) и новое значение очков (целое число) для установки.
В вашем основном файле создайте Score с начальными очками, выведите текущее значение с помощью геттера, обновите его с помощью сеттера новым значением, выведите обновленное значение, затем освободите score и выведите подтверждение.
Выводите результат в следующем формате:
Initial: {points}
Updated: {points}
FreedНапример, при входных данных 100 и 250 результат будет таким:
Initial: 100
Updated: 250
FreedОбратите внимание, что main.c не может получить прямой доступ к points — он должен использовать ваши функции геттера и сеттера. Это инкапсуляция в действии: вы точно контролируете, как скрытые данные могут быть прочитаны и изменены.
Шпаргалка
При использовании непрозрачных указателей (opaque pointers) члены структуры скрыты от внешнего кода. Чтобы получить доступ к этим приватным данным или изменить их, используйте функции геттеры (getter) и сеттеры (setter).
Геттер возвращает значение скрытого члена и использует указатель const, так как он не изменяет объект:
// counter.h
int counter_get_value(const Counter *c);
// counter.c
int counter_get_value(const Counter *c) {
return c->value;
}
Сеттер изменяет значение скрытого члена:
// counter.h
void counter_set_value(Counter *c, int new_value);
// counter.c
void counter_set_value(Counter *c, int new_value) {
c->value = new_value;
}
Используйте соглашение об именовании с именем типа в качестве префикса (counter_), чтобы избежать конфликтов и сделать API более понятным.
Внешний код взаимодействует со скрытыми данными через эти функции:
// main.c
Counter *c = create_counter(10);
printf("%d\n", counter_get_value(c)); // 10
counter_set_value(c, 25);
printf("%d\n", counter_get_value(c)); // 25
Попробуйте сами
#include <stdio.h>
#include "score.h"
int main() {
int initial_points, new_points;
scanf("%d", &initial_points);
scanf("%d", &new_points);
// TODO: Создать Score с initial_points
// TODO: Вывести начальное значение, используя геттер
// Формат: "Initial: {points}"
// TODO: Обновить счет, используя сеттер с new_points
// TODO: Вывести обновленное значение, используя геттер
// Формат: "Updated: {points}"
// TODO: Освободить память score и вывести "Freed"
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация круга