Ключевое слово 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.