Константные функции-члены
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 29 из 104.
В предыдущем уроке ты видел ключевое слово const в методах-геттерах. Но что именно означает, когда const появляется после списка параметров функции? const member function обещает не изменять переменные-члены объекта.
class Rectangle {
int width;
int height;
public:
Rectangle(int w, int h) : width(w), height(h) {}
int getArea() const { // const функция-член
return width * height; // Чтение членов разрешено
}
void setWidth(int w) { // Non-const - изменяет объект
width = w;
}
};const после списка параметров сообщает компилятору: «Эта function не изменит состояние объекта». Если попытаться изменить переменную-член внутри const function, компилятор выдаст ошибку.
Это становится особенно важно при работе с объектами const или ссылками const. Объект const может вызывать только const member function:
void printArea(const Rectangle& rect) {
std::cout << rect.getArea(); // OK - getArea() является const
// rect.setWidth(10); // ERROR - setWidth() не является const
}Помечать функции как const, если они не изменяют объект, — хорошая практика. Это документирует ваше намерение, позволяет функции работать с объектами const и помогает компилятору обнаруживать случайные изменения. Любую member-функцию, которая только считывает данные, следует помечать как const.
Задание
ЛегкоСоздадим конвертер температуры, демонстрирующий, когда и зачем использовать const member functions. Ты создашь класс, в котором одни методы только читают данные (и должны быть const), а другие изменяют состояние объекта.
Ты создашь два файла, чтобы организовать свой код:
Temperature.h: определи классTemperature, который хранит значение температуры и предоставляет различные способы его чтения и изменения. В твоём классе должны быть:- Приватный member
celsius(double) для хранения температуры - Constructor, принимающий Initial значение по шкале Цельсия
- Метод
getCelsius(), возвращающий сохранённое значение. Он должен быть const, поскольку только читает данные - Метод
getFahrenheit(), вычисляющий и возвращающий температуру по шкале Фаренгейта с использованием Formulacelsius * 9.0 / 5.0 + 32.0; он также должен быть const, поскольку ничего не изменяет - Метод
getKelvin(), возвращающий температуру по шкале Кельвина с использованиемcelsius + 273.15; он также должен быть const - Метод
setCelsius(double value), обновляющий сохранённую температуру. Он не может быть const, поскольку изменяет объект - Метод
adjustBy(double delta), добавляющий delta к текущей температуре; он также не должен быть const
- Приватный member
main.cpp: продемонстрируй, как работают const member functions с обычными и const объектами. Считай Initial значение температуры из input, затем:- Create объект
Temperatureс введённым значением - Выведи
"Initial: <celsius>C = <fahrenheit>F = <kelvin>K" - Увеличь температуру на
10.0degrees - Выведи
"After adjustment: <celsius>C" - Создай функцию-помощник
void printReadings(const Temperature& temp), которая принимает const ссылку и выводит"Reading: <celsius>C, <fahrenheit>F". Эта функция может вызывать для temp только const методы - Вызови
printReadings()со своим объектом температуры - Установи температуру равной
0.0(точка Freezing) - Выведи
"Freezing point: <celsius>C = <fahrenheit>F"
- Create объект
Форматируй все значения температуры с одним десятичным знаком, используя std::fixed и std::setprecision(1) из <iomanip>.
Ключевая идея заключается в том, что printReadings() получает const ссылку, поэтому может вызывать только методы, помеченные как const. Именно поэтому важно правильно помечать методы доступа как const. Это позволяет им работать в контекстах, где объект нельзя изменить.
Попробуйте сами
#include <iostream>
#include <iomanip>
#include "Temperature.h"
using namespace std;
// TODO: Реализуйте методы класса Temperature здесь
// Конструктор
Temperature::Temperature(double initialCelsius) {
// TODO: Инициализируйте celsius
}
// TODO: Реализуйте getCelsius() как const
// TODO: Реализуйте getFahrenheit() как const
// TODO: Реализуйте getKelvin() как const
// TODO: Реализуйте setCelsius(double value)
// TODO: Реализуйте adjustBy(double delta)
// TODO: Создайте вспомогательную функцию printReadings, которая принимает const Temperature&
// и выводит "Reading: <celsius>C, <fahrenheit>F"
// Note: Эта функция может вызывать только const-методы у temp!
int main() {
double initialTemp;
cin >> initialTemp;
// Установите форматирование вывода
cout << fixed << setprecision(1);
// TODO: Создайте объект Temperature с входным значением
// TODO: Выведите "Initial: <celsius>C = <fahrenheit>F = <kelvin>K"
// TODO: Измените температуру на 10.0 градусов
// TODO: Print "After adjustment: <celsius>C"
// TODO: Вызовите printReadings() с вашим объектом Temperature
// TODO: Set the temperature to 0.0 (freezing point)
// TODO: Print "Freezing point: <celsius>C = <fahrenheit>F"
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++