Ключевое слово mutable
Про ключевое слово mutable знают немногие разработчики, однако оно может быть очень полезным в ряде случаев. В этой статье мы рассмотрим случай, когда семантическая константность не эквивалентна синтаксической константности, и ключевое слово mutable позволит решить эту проблему. Но давайте обо всем по-порядку...
Бывают ситуации, когда нам нужно изменить состояние объекта, при этом сохранив видимое извне состояние объекта константным. Изменение объекта может понадобиться по каким-либо техническим причинам, но клиенты (пользователи объекта) не должны этого заметить. Ярким примером такого рода задач является кеширование данных.
Рассмотрим класс Date:
class Date { int day; int month; int year; public: Date(int d, int m, int y) : day(d), month(m), year(y) { } string toString() const { convertToString(); } void setDate(int d, int m, int y){ day = d; month = m; year = y; } // ... };
Предположим, что метод convertToString
- это ресурсозатратная операция, которую мы не хотим вызывать каждый раз при вызове метода toString.
Если дата не поменялась, то зачем вычислять строковое представление даты опять?! Можно один раз сохранить строку в переменной и возвращать ее каждый раз до тех пор, пока дата не поменяется. В результате у нас будет кеширование строки и очистка кеша при изменении даты.
class Date { int day; int month; int year; string cache = ""; public: Date(int d, int m, int y) : day(d), month(m), year(y) { } string toString() const { if(cache.empty()) cache = convertToString(); return cache; } void setDate(int d, int m, int y){ day = d; month = m; year = y; resetCache(); } void resetCache(){ cache = ""; } // ... };
Вроде бы все хорошо. Мы кешировали строковое представление даты и теперь вызываем функцию convertToString
только тогда, когда это действительно необходимо. Однако есть проблема... Этот код не скомпилируется! Дело в том, что метод toString
константный, но он изменяет переменную cache. Если убрать const
из объявления метода, то клиенты нашего класса не поймут. Ведь toString
- это простой геттер, данная функция точно не должна ничего менять в классе. Почему же тогда нет const
в ее объявлении?
В этой проблеме нам поможет ключевое слово mutable. Оно добавляется в объявлении к переменным членам класса и указывает, что данная переменная может изменяться в константном методе. Вот готовый пример:
class Date { int day; int month; int year; mutable string cache = ""; public: Date(int d, int m, int y) : day(d), month(m), year(y) { } string toString() const { if(cache.empty()) cache = convertToString(); return cache; } void setDate(int d, int m, int y){ day = d; month = m; year = y; resetCache(); } void resetCache(){ cache = ""; } // ... };
Помимо рассмотренного случая, mutable используется в лямбда-функциях для захвата переменных по значению, которые потом можно будет изменить в теле лямбды.
Ключевое слово mutable
играет важную роль в написании чистого кода. Если метод в вашем классе по логике должен быть константным, ставьте const
. При этом если он изменяет какие-то внутренние поля, то помечайте их словом mutable
.