Menu
Coddy logo textTech

Ключевое слово mutable

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

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

Член mutable можно изменять даже внутри константной функции-члена:

class DataFetcher {
    std::string data;
    mutable int accessCount;    // Может быть изменён в const-функциях
    
public:
    DataFetcher(std::string d) : data(d), accessCount(0) {}
    
    std::string getData() const {
        accessCount++;          // Разрешено, потому что accessCount является mutable
        return data;
    }
    
    int getAccessCount() const {
        return accessCount;
    }
};

Без mutable увеличение accessCount внутри const-функции getData() вызвало бы ошибку компилятора. Ключевое слово mutable сообщает компилятору: «Этому члену разрешено изменяться, даже когда объект считается const».

Распространённые варианты использования изменяемых объектов включают кэширование вычисленных значений, отслеживание статистики обращений и примитивы синхронизации, такие как мьютексы. В каждом случае изменение не меняет того, что объект логически представляет собой, — кэшированное значение является лишь оптимизацией, а счётчик обращений — метаданными об использовании.

class Circle {
    double radius;
    mutable double cachedArea;
    mutable bool areaCached;
    
public:
    double getArea() const {
        if (!areaCached) {
            cachedArea = 3.14159 * radius * radius;
            areaCached = true;
        }
        return cachedArea;
    }
};

Используйте mutable экономно — его следует применять только к членам, которые не влияют на наблюдаемое состояние объекта.

challenge icon

Задание

Легко

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

Вы создадите два файла, чтобы организовать код:

  • Document.h: Определите Document class, который хранит содержимое и незаметно отслеживает статистику просмотров. Вашему class необходимы:
    • Закрытые члены: title (строка), content (строка) и mutable int viewCount, который можно изменять даже в const-методах
    • Конструктор, принимающий заголовок и содержимое и инициализирующий viewCount значением 0
    • Const-метод getTitle(), возвращающий заголовок
    • Const-метод getContent(), увеличивающий viewCount (это разрешено, потому что он mutable) и возвращающий содержимое
    • Const-метод getViewCount(), возвращающий количество обращений к содержимому
    • Const-метод getPreview(), возвращающий первые 20 символов содержимого, за которыми следует "...". Этот метод НЕ должен увеличивать счётчик просмотров, поскольку это всего лишь предварительный просмотр
  • main.cpp: Продемонстрируйте, как mutable позволяет отслеживать статистику обращений, сохраняя логическую константность методов. Считайте заголовок и содержимое документа из input (каждое значение находится на отдельной строке), затем:
    • Создайте Document с введёнными значениями
    • Выведите "Title: <title>"
    • Выведите "Preview: <preview>"
    • Выведите "Views after preview: <count>"
    • Получите полное содержимое с помощью getContent() и выведите "Content: <content>"
    • Выведите "Views after first read: <count>"
    • Снова получите содержимое и выведите "Content: <content>"
    • Выведите "Views after second read: <count>"
    • Создайте вспомогательную функцию void displayDocument(const Document& doc), которая выводит "[Const Access] <title>: <content>". Это подтверждает, что наши const-методы работают с const-ссылками
    • Вызовите displayDocument() для своего документа
    • Выведите "Final view count: <count>"

Счётчик просмотров должен иметь значение 0 после предварительного просмотра (поскольку предварительные просмотры не считаются полными просмотрами), а затем увеличиваться при каждом вызове getContent(). Ключевое слово mutable делает это возможным; без него вы не смогли бы изменять viewCount внутри const-методов, таких как getContent().

Если содержимое короче 20 символов, getPreview() должен возвращать всё содержимое, за которым следует "...".

Попробуйте сами

#include <iostream>
#include <string>
#include "Document.h"
using namespace std;

// TODO: Реализуйте функцию displayDocument
// Она должна выводить: "[Const Access] <title>: <content>"
void displayDocument(const Document& doc) {
    // TODO: Реализуйте эту функцию
}

int main() {
    // Прочитать ввод
    string title;
    string content;
    getline(cin, title);
    getline(cin, content);
    
    // TODO: Создайте Document с входными значениями
    
    // TODO: Выведите "Title: <title>"
    
    // TODO: Выведите "Preview: <preview>"
    
    // TODO: Print "Views after preview: <count>"
    
    // TODO: Получите полный контент и выведите "Content: <content>"
    
    // TODO: Print "Views after first read: <count>"
    
    // TODO: Снова получите контент и выведите "Content: <content>"
    
    // TODO: Print "Views after second read: <count>"
    
    // TODO: Вызовите displayDocument() с вашим документом
    
    // TODO: Print "Final view count: <count>"
    
    return 0;
}
quiz iconПроверьте себя

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

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

Потренируйтесь самостоятельно: Онлайн-компилятор C++