Ключевое слово mutable
Ключевое слово mutable в C++ позволяет объявить поле класса
изменяемым даже в том случае, когда объект этого класса является
константным. Обычно поля класса, помеченные как mutable,
используются для кэширования, счётчиков вызовов, ленивых
вычислений и других вспомогательных данных, которые не влияют
на логическое состояние объекта. Ключевое слово mutable
записывается перед типом поля внутри объявления класса.
Синтаксис
class ClassName
{
mutable тип имя_поля;
};
Пример
Давайте создадим класс Counter с полем count,
помеченным как mutable, и попробуем изменить это поле
внутри константного метода:
#include <iostream>
using namespace std;
class Counter
{
public:
mutable int count = 0;
void increment() const
{
count++;
}
};
int main()
{
const Counter c;
c.increment();
c.increment();
cout << c.count << endl;
return 0;
}
Результат выполнения кода:
2
Без ключевого слова mutable этот код не скомпилировался бы,
так как метод increment является константным, а значит,
не имеет права изменять поля объекта.
Пример
Давайте рассмотрим классический пример использования mutable
для кэширования результатов вычислений. Создадим класс
Squares, который запоминает последний результат:
#include <iostream>
using namespace std;
class Squares
{
private:
mutable int cache = 0;
mutable bool cached = false;
public:
int square(int x) const
{
if (cached && cache == x * x) {
cout << "from cache" << endl;
return cache;
}
cache = x * x;
cached = true;
cout << "computed" << endl;
return cache;
}
};
int main()
{
const Squares s;
cout << s.square(3) << endl;
cout << s.square(3) << endl;
return 0;
}
Результат выполнения кода:
"computed"
9
"from cache"
9
Здесь поля cache и cached помечены как mutable,
поэтому их можно изменять внутри константного метода square,
хотя сам объект s объявлен как const.
Пример
Давайте создадим класс Logger, который считает количество
вызовов своего метода через mutable-поле:
#include <iostream>
#include <string>
using namespace std;
class Logger
{
private:
mutable int calls = 0;
public:
void log(const string& msg) const
{
calls++;
cout << msg << endl;
}
int getCalls() const
{
return calls;
}
};
int main()
{
const Logger logger;
logger.log("abcde");
logger.log("12345");
cout << logger.getCalls() << endl;
return 0;
}
Результат выполнения кода:
"abcde"
12345
2