Имеет ли ключевое слово "mutable" какую-либо цель, кроме разрешения переменной быть измененной функцией const?
некоторое время назад я наткнулся на код, который ознаменовал собой переменную-член класса с mutable ключевое слово. Насколько я вижу, это просто позволяет вам изменять переменную в const способ:
class Foo
{
private:
mutable bool done_;
public:
void doSomething() const { ...; done_ = true; }
};
это только использование этого сайта или есть больше, чем кажется на первый взгляд? С тех пор я использовал эту технику в классе, отметив a boost::mutex как изменчивое разрешение const функции, чтобы заблокировать его по соображениям безопасности потока, но, честно говоря, это похоже на немного мотыга.
18 ответов:
это позволяет дифференцировать побитовое const и логическое const. Логическая константа - это когда объект не изменяется таким образом, который виден через открытый интерфейс, как ваш пример блокировки. Другим примером может быть класс, который вычисляет значение при первом запросе и кэширует результат.
начиная с c++11
mutableможно использовать на лямбде для обозначения того, что вещи, захваченные значением, могут быть изменены (они не являются по умолчанию):int x = 0; auto f1 = [=]() mutable {x = 42;}; // OK auto f2 = [=]() {x = 42;}; // Error: a by-value capture cannot be modified in a non-mutable lambda
The
mutableключевое слово-это способ проткнутьconstвуаль повесить над объектом. Если у вас есть ссылка const или указатель на объект, вы не можете изменить этот объект каким-либо образом за исключением когда и как отмечаетсяmutable.С
constссылка или указатель, вы должны:
- доступ только для чтения для любых видимых членов данных
- разрешение на вызов только методов, помеченных как
const.The
mutableисключение делает его, так что теперь вы можете писать или устанавливать элементы данных, которые помеченыmutable. Это единственная внешне видимая разница.внутри этих
constметоды, которые видны вам также можно записать в элементы данных, которые помеченыmutable. По существу, завеса const прокалывается всесторонне. Это полностью зависит от конструктора API, чтобы гарантировать, чтоmutableне уничтожитьconstконцепция и используется только в полезных особый случай. Элементmutableключевое слово помогает, потому что оно четко маркирует элементы данных, которые подвержены этим особым случаям.на практике вы можете использовать
constнавязчиво по всей вашей кодовой базе (вы по существу хотите "заразить" свою кодовую базу с помощьюconst"болезнь"). В этом мире указатели и ссылкиconstС очень немногими исключениями, возвращая код, который легче рассуждать и понимать. Для интересного отступления посмотрите "ссылочную прозрачность".без
mutableключевое слово вы в конечном итоге будете вынуждены использоватьconst_castдля обработки различных полезных специальных случаев, которые он позволяет (кэширование, подсчет ссылок, отладочные данные и т. д.). К сожалениюconst_castзначительно более разрушительно, чемmutableпотому что это заставляет API клиент уничтожитьconstзащита объектов, которые он использует. Кроме того, это вызывает широкое распространениеconstразрушение:const_casting указатель const или ссылка позволяет беспрепятственно запись и вызов метода доступ к видимым элементам. Напротивmutableтребуется, чтобы разработчик API осуществлял мелкозернистый контроль надconstисключения, и обычно эти исключения скрыты вconstметоды, работающие с частными данными.(N. B. Я имею в виду данные и метод видимость несколько раз. Я про члены, помеченные как public и private или protected которая представляет собой совершенно иной тип защиты объекта обсуждается здесь.)
ваше использование с boost:: mutex-это именно то, для чего предназначено это ключевое слово. Другое использование - внутреннее кэширование результатов для ускорения доступа.
в принципе, 'mutable' применяется к любому атрибуту класса, который не влияет на внешне видимое состояние объекта.
в примере кода в вашем вопросе mutable может быть неуместным, если значение done_ влияет на внешнее состояние, оно зависит от того, что находится в нем ...; часть.
Mutable предназначен для маркировки конкретного атрибута как изменяемого изнутри
constметоды. Это его единственная цель. Подумайте хорошенько, прежде чем использовать его, потому что ваш код, вероятно, будет чище и более читабельным, если вы измените дизайн, а не используетеmutable.http://www.highprogrammer.com/alan/rants/mutable.html
Так что если выше безумие не то, что изменчивость-это для чего? Это место тонкий случай: mutable для случай, когда объект логически постоянно, но на практике необходимо изменение. Таких случаев немного и далеко между ними, но они существуют.
примеры, которые приводит автор, включают кэширование и временные отладочные переменные.
это полезно в ситуациях, когда у вас есть скрытое внутреннее состояние, такое как кэш. Например:
class HashTable { ... public: string lookup(string key) const { if(key == lastKey) return lastValue; string value = lookupInternal(key); lastKey = key; lastValue = value; return value; } private: mutable string lastKey, lastValue; };и тогда вы можете иметь
const HashTableобъект по-прежнему использовать егоlookup()метод, который изменяет внутренний кэш.
Ну, да, это то, что он делает. Я использую его для членов, которые изменяются методами, которые не логически изменить состояние класса - например, чтобы ускорить поиски путем реализации кэша:
class CIniWrapper { public: CIniWrapper(LPCTSTR szIniFile); // non-const: logically modifies the state of the object void SetValue(LPCTSTR szName, LPCTSTR szValue); // const: does not logically change the object LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const; // ... private: // cache, avoids going to disk when a named value is retrieved multiple times // does not logically change the public interface, so declared mutable // so that it can be used by the const GetValue() method mutable std::map<string, string> m_mapNameToValue; };Теперь вы должны использовать это с осторожностью - проблемы параллелизма являются большой проблемой, так как вызывающий может предположить, что они потокобезопасны, если только использовать
constметоды. И, конечно же, изменениеmutableданные не должны изменять поведение объекта в любом случае значительная мода, что-то, что может быть нарушено примером, который я дал, если, например, ожидалось, что изменения, записанные на диск, будут немедленно видны приложению.
mutableсуществует, как вы предполагаете, чтобы позволить изменять данные в другой постоянной функции.цель состоит в том, что у вас может быть функция, которая "ничего не делает" с внутренним состоянием объекта, и поэтому вы отмечаете функцию
const, но вам действительно может потребоваться изменить состояние некоторых объектов таким образом, чтобы это не повлияло на его правильную функциональность.ключевое слово может действовать как подсказка компилятору -- теоретический компилятор может разместить постоянный объект (например, глобальный) в памяти, которая была отмечена только для чтения. Наличие
mutableнамекает, что этого делать не следует.вот некоторые веские причины для объявления и использования изменяемых данных:
- потокобезопасность. Объявление
mutable boost::mutex- это вполне разумно.- статистика. Подсчет количества вызовов функции с учетом некоторых или всех ее аргументов.
- мемоизация. Вычисление какого-то дорогостоящего ответа, а затем сохранение его для дальнейшего использования вместо того, чтобы пересчитывать его снова.
Mutable используется, когда у вас есть переменная внутри класса, которая используется только в этом классе, чтобы сигнализировать такие вещи, как, например, мьютекс или блокировка. Эта переменная не изменяет поведение класса, но необходима для реализации потокобезопасности самого класса. Таким образом, если бы не было "изменчивого", вы не смогли бы иметь функции "const", потому что эта переменная должна быть изменена во всех функциях, доступных внешнему миру. Поэтому изменчивым был введено для того, чтобы сделать переменную-член доступной для записи даже с помощью функции const.
указанный изменяемый параметр сообщает как компилятору, так и читателю, что он безопасно и ожидается, что переменная-член может быть изменена в пределах const функция-член.
mutable в основном используется на детали реализации класса. Пользователю класса не нужно знать об этом, поэтому он думает, что метод "должен" быть const может быть. Ваш пример того, что мьютекс может быть изменяемым, является хорошим каноническим примером.
ваше использование это не хак, хотя, как и многие вещи в C++, mutable can будьте взломаны для ленивого программиста, который не хочет возвращаться назад и отмечать что-то, что не должно быть const как неконстант.
используйте "изменяемый", когда для вещей, которые логически не имеют состояния для пользователя (и, следовательно, должны иметь геттеры "const" в API-интерфейсах общедоступного класса), но не имеют состояния в базовой реализации (код в вашем .СРР.)
случаи, когда я использую его чаще всего, - это ленивая инициализация членов "простых старых данных" без состояния. А именно, он идеально подходит в узких случаях, когда такие элементы дорого либо строить (процессор) , либо носить с собой (память) и многие пользователи объекта никогда не будет просить о них. В этой ситуации вы хотите ленивую конструкцию на заднем конце для производительности, так как 90% построенных объектов никогда не нужно будет строить их вообще, но вам все равно нужно представить правильный API без состояния для общественного потребления.
в некоторых случаях (например, плохо спроектированные итераторы) класс должен сохранять счетчик или какое-либо другое случайное значение, которое на самом деле не влияет на основное "состояние" класса. Это чаще всего, где я вижу mutable используется. Без mutable, вы были бы вынуждены пожертвовать всей константой вашего дизайна.
Это похоже на хак большую часть времени для меня, а также. Полезно в очень немногих ситуациях.
классический пример (как упоминалось в других ответах) и единственная ситуация, которую я видел
mutableключевое слово, используемое до сих пор, предназначено для кэширования результата сложногоGetметод, где кэш реализован как элемент данных класса, а не как статическая переменная в методе (по причинам совместного использования нескольких функций или простой чистоты).В общем, альтернативы использования
mutableключевое слово обычно является статической переменной в методе илиconst_cast- трик.еще одно подробное объяснение находится в здесь.
ключевое слово mutable очень полезно при создании заглушек для целей тестирования класса. Вы можете заглушить функцию const и по-прежнему иметь возможность увеличивать (изменяемые) счетчики или любые функции тестирования, которые вы добавили в свою заглушку. Это держит интерфейса стаба класс нетронутым.
Mutable изменяет значение
constот побитового const к логическому const для класса.это означает, что классы с изменяемыми членами больше не являются побитовыми const и больше не будут отображаться в разделах исполняемого файла только для чтения.
кроме того, он изменяет проверку типов, позволяя
constфункции-члены для изменения изменяемых члена без использованияconst_cast.class Logical { mutable int var; public: Logical(): var(0) {} void set(int x) const { var = x; } }; class Bitwise { int var; public: Bitwise(): var(0) {} void set(int x) const { const_cast<Bitwise*>(this)->var = x; } }; const Logical logical; // Not put in read-only. const Bitwise bitwise; // Likely put in read-only. int main(void) { logical.set(5); // Well defined. bitwise.set(5); // Undefined. }посмотреть другие ответы для более подробной информации, но я хотел подчеркнуть, что это не просто для безопасности типа, и это влияет на скомпилированный результат.
изменяемая может быть удобна, когда вы переопределяете виртуальную функцию const и хотите изменить свою дочернюю переменную-член класса в этой функции. В большинстве случаев вы не хотите изменять интерфейс базового класса, поэтому вам нужно использовать собственную изменяемую переменную-член.
само ключевое слово 'mutable' на самом деле является зарезервированным ключевым словом.часто он используется для изменения значения постоянной переменной.Если вы хотите иметь несколько значений constsnt, используйте ключевое слово mutable.
//Prototype class tag_name{ : : mutable var_name; : : };
один из лучших примеров, где мы используем mutable является, в глубокой копии. в конструктор копирования мы отправляем
const &objв качестве аргумента. Таким образом, новый созданный объект будет иметь постоянный тип. Если мы хотим изменить (в основном мы не будем меняться, в редких случаях мы можем изменить) члены в этом недавно созданном объекте const нам нужно объявить его какmutable.
mutableкласса памяти может быть использован только на не статичный, не являющихся членами константные данные класса. Изменяемый элемент данных класса может быть изменен, даже если он часть объекта, который объявлен как const.class Test { public: Test(): x(1), y(1) {}; mutable int x; int y; }; int main() { const Test object; object.x = 123; //object.y = 123; /* * The above line if uncommented, will create compilation error. */ cout<< "X:"<< object.x << ", Y:" << object.y; return 0; } Output:- X:123, Y:1в приведенном выше примере мы можем изменить значение переменной-члена
xхотя это часть объекта, который объявлен как const. Это потому, что переменнаяxобъявляется изменяемым. Но если вы попытаетесь изменить значение переменной-членаy, компилятор выдаст ошибку.
Comments