Menu
Coddy logo textTech

Константные функции-члены

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

Вы видели ключевое слово const в методах-геттерах в предыдущем уроке. Но что именно это означает, когда const появляется после списка параметров функции? Константная функция-член обещает не изменять никакие переменные-члены объекта.

class Rectangle {
    int width;
    int height;
public:
    Rectangle(int w, int h) : width(w), height(h) {}
    
    int getArea() const {      // константная функция-член
        return width * height;  // Чтение членов разрешено
    }
    
    void setWidth(int w) {     // Неконстантная — изменяет объект
        width = w;
    }
};

Ключевое слово const после списка параметров сообщает компилятору: «Эта функция не изменит состояние объекта». Если вы попытаетесь изменить переменную-член внутри константной функции, компилятор выдаст ошибку.

Это становится необходимым при работе с объектами const или ссылками const. Объект const может вызывать только функции-члены const:

void printArea(const Rectangle& rect) {
    std::cout << rect.getArea();    // OK - getArea() является константным
    // rect.setWidth(10);           // ОШИБКА - setWidth() не является константным
}

Пометка функций как const, когда они не изменяют объект, является хорошей практикой. Это документирует ваши намерения, позволяет функции работать с константными объектами и помогает компилятору отлавливать случайные изменения. Любая функция-член, которая только считывает данные, должна быть помечена как const.

challenge icon

Задание

Легко

Давайте создадим конвертер температуры, который продемонстрирует, когда и почему следует использовать константные функции-члены (const member functions). Вы создадите класс, в котором одни методы только считывают данные (и должны быть const), а другие изменяют состояние объекта.

Для организации кода вы создадите два файла:

  • Temperature.h: Определите класс Temperature, который хранит значение температуры и предоставляет различные способы его чтения и изменения. Ваш класс должен содержать:
    • Приватный член celsius (double) для хранения температуры
    • Конструктор, принимающий начальное значение в градусах Цельсия
    • Метод getCelsius(), который возвращает сохраненное значение — он должен быть const, так как только считывает данные
    • Метод getFahrenheit(), который вычисляет и возвращает температуру в Фаренгейтах по формуле celsius * 9.0 / 5.0 + 32.0 — также const, так как он ничего не изменяет
    • Метод getKelvin(), который возвращает температуру в Кельвинах по формуле celsius + 273.15 — тоже const
    • Метод setCelsius(double value), который обновляет сохраненную температуру — он не может быть const, так как изменяет объект
    • Метод adjustBy(double delta), который добавляет дельту к текущей температуре — также не является константным
  • main.cpp: Продемонстрируйте, как константные функции-члены работают как с обычными, так и с константными объектами. Считайте начальное значение температуры из входных данных, затем:
    • Создайте объект Temperature с введенным значением
    • Выведите "Initial: <celsius>C = <fahrenheit>F = <kelvin>K"
    • Измените температуру на 10.0 градусов
    • Выведите "After adjustment: <celsius>C"
    • Создайте вспомогательную функцию void printReadings(const Temperature& temp), которая принимает константную ссылку и выводит "Reading: <celsius>C, <fahrenheit>F" — эта функция может вызывать только константные методы у объекта temp
    • Вызовите printReadings() со своим объектом температуры
    • Установите температуру на 0.0 (точка замерзания)
    • Выведите "Freezing point: <celsius>C = <fahrenheit>F"

Форматируйте все значения температуры с одним десятичным знаком, используя std::fixed и std::setprecision(1) из библиотеки <iomanip>.

Ключевой момент здесь заключается в том, что printReadings() получает константную ссылку, поэтому она может вызывать только те методы, которые помечены как const. Вот почему важно правильно помечать методы-геттеры как const — это позволяет им работать в контекстах, где объект не может быть изменен.

Шпаргалка

Константная функция-член гарантирует, что она не будет изменять никакие переменные-члены объекта. Ключевое слово const ставится после списка параметров функции:

class Rectangle {
    int width;
    int height;
public:
    int getArea() const {      // константная функция-член
        return width * height;  // Чтение членов разрешено
    }
    
    void setWidth(int w) {     // Неконстантная — изменяет объект
        width = w;
    }
};

Константные функции-члены могут только читать переменные-члены, но не изменять их. Если вы попытаетесь изменить переменную-член внутри константной функции, компилятор выдаст ошибку.

Константный объект или константная ссылка могут вызывать только константные функции-члены:

void printArea(const Rectangle& rect) {
    std::cout << rect.getArea();    // OK — getArea() является константной
    // rect.setWidth(10);           // ОШИБКА — setWidth() не является константной
}

Помечайте функции как 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: Выведите "After adjustment: <celsius>C"

    // TODO: Вызовите printReadings() с вашим объектом температуры

    // TODO: Установите температуру на 0.0 (точка замерзания)

    // TODO: Выведите "Freezing point: <celsius>C = <fahrenheit>F"

    return 0;
}
quiz iconПроверьте себя

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

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