Константная корректность
Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 8 из 61.
При написании функций, которые действуют как методы, некоторые из них должны изменять объект (например, player_take_damage), в то время как другие должны только считывать данные, ничего не меняя. Ключевое слово const позволяет обеспечить это различие на уровне компилятора.
Помечая параметр-указатель как const, вы гарантируете, что функция не будет изменять данные, на которые он указывает. Если вы случайно попытаетесь изменить член, компилятор выдаст ошибку:
typedef struct {
int x;
int y;
} Point;
// Эта функция может только ЧИТАТЬ точку
void point_print(const Point *self) {
printf("(%d, %d)\n", self->x, self->y);
// self->x = 10; // ОШИБКА: невозможно изменить через константный указатель
}
// Эта функция может ИЗМЕНЯТЬ точку
void point_move(Point *self, int dx, int dy) {
self->x += dx;
self->y += dy;
}
Параметр const Point *self сообщает как компилятору, так и другим программистам, что point_print является «геттером» или операцией только для чтения. Это называется константной корректностью — использованием const везде, где модификация не требуется.
Эта практика позволяет выявлять ошибки на ранних стадиях. Если вы пишете функцию отображения и случайно изменяете поле, компилятор немедленно остановит вас, не позволяя ошибке просочиться в вашу программу.
Задание
ЛегкоДавайте создадим модуль Temperature, который хранит значение температуры и предоставляет операции как для чтения, так и для изменения. Это поможет вам попрактиковаться в использовании указателей const, чтобы четко разграничить функции, которые только читают данные, и те, которые их изменяют.
Вы создадите три файла:
temperature.h: Объявите структуруTemperatureс единственным полемdouble celsius. Также объявите три функции:temp_display— принимает указательconstна Temperature и выводит текущую температуру (только для чтения)temp_adjust— принимает обычный указатель на Temperature и значение типа double, затем добавляет это значение к celsius (изменение)temp_to_fahrenheit— принимает указательconstна Temperature и возвращает значение, преобразованное в градусы Фаренгейта (только для чтения)
TEMPERATURE_H.temperature.c: Реализуйте все три функции. Функцияtemp_displayдолжна выводить данные в формате"Current: %.1fC\n". Функцияtemp_to_fahrenheitдолжна возвращатьcelsius * 9.0 / 5.0 + 32.0. Помните, что функции, использующие указателиconst, не могут изменять данные структуры.main.c: Создайте Temperature, выполните операции и выведите результаты.
Вы получите два входных значения: начальное значение в градусах Цельсия (как double) и величину корректировки (как double).
В вашем основном файле (main):
- Инициализируйте
Temperatureначальным значением Цельсия - Вызовите
temp_display, чтобы показать начальную температуру - Вызовите
temp_adjust, чтобы изменить температуру на величину корректировки - Снова вызовите
temp_display, чтобы показать обновленную температуру - Выведите результат преобразования в Фаренгейты, используя
temp_to_fahrenheit
Выводите результаты в следующем формате:
Current: {celsius}C
Current: {celsius}C
Fahrenheit: {fahrenheit}FИспользуйте %.1f для всех значений температуры, чтобы отображать один десятичный знак.
Например, при начальном значении 20.0 и корректировке 5.0 вывод будет следующим:
Current: 20.0C
Current: 25.0C
Fahrenheit: 77.0FШпаргалка
Ключевое слово const может использоваться с параметрами-указателями, чтобы указать, что функция не будет изменять данные, на которые указывает указатель. Это называется const-корректностью.
Когда параметр-указатель помечен как const, компилятор будет предотвращать любые изменения данных через этот указатель:
typedef struct {
int x;
int y;
} Point;
// Функция только для чтения — не может изменять точку
void point_print(const Point *self) {
printf("(%d, %d)\n", self->x, self->y);
// self->x = 10; // ОШИБКА: нельзя изменять через const-указатель
}
// Модифицирующая функция — может изменять точку
void point_move(Point *self, int dx, int dy) {
self->x += dx;
self->y += dy;
}
Использование const Point *self сообщает как компилятору, так и другим программистам, что функция является операцией только для чтения. Это помогает выявлять ошибки на ранних стадиях: если вы случайно попытаетесь изменить данные в функции, которая не должна ничего менять, компилятор немедленно выдаст ошибку.
Лучшая практика: используйте const для параметров-указателей всякий раз, когда функции не нужно изменять данные. Это делает намерения вашего кода более понятными и предотвращает случайные изменения.
Попробуйте сами
#include <stdio.h>
#include "temperature.h"
int main() {
double initial_celsius, adjustment;
scanf("%lf", &initial_celsius);
scanf("%lf", &adjustment);
// TODO: Создайте структуру Temperature и инициализируйте ее значением initial_celsius
// TODO: Вызовите temp_display, чтобы показать начальную температуру
// TODO: Вызовите temp_adjust, чтобы изменить температуру на величину adjustment
// TODO: Снова вызовите temp_display, чтобы показать обновленную температуру
// TODO: Выведите значение в Фаренгейтах, используя temp_to_fahrenheit
// Формат: "Fahrenheit: %.1fF\n"
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация круга