Некоторое время назад я наткнулся на некоторый код, который помечал переменную-член класса mutable
ключевым словом. Насколько я вижу, это просто позволяет вам изменить переменную в const
методе:
class Foo
{
private:
mutable bool done_;
public:
void doSomething() const { ...; done_ = true; }
};
Это единственное использование этого ключевого слова или это больше, чем кажется на первый взгляд? С тех пор я использовал эту технику в классе, помечая boost::mutex
как изменяемые, позволяя const
функциям блокировать ее по соображениям безопасности потоков, но, честно говоря, это похоже на хак.
mutable
: stackoverflow.com/questions/15999123/…const
(типов), поэтому мне не нужно делать это:class A_mutable{}; using A = A_mutable const; mutable_t<A> a;
если я хочу const-по-умолчанию, то естьmutable A a;
(явное изменение) иA a;
(неявное const).Ответы:
Это позволяет дифференцировать побитовый констант и логический констант. Логическая константа - это когда объект не изменяется таким образом, который виден через открытый интерфейс, как ваш пример блокировки. Другим примером может быть класс, который вычисляет значение при первом запросе и кэширует результат.
Поскольку c ++ 11
mutable
может использоваться в лямбда-выражении для обозначения того, что вещи, захваченные по значению, являются изменяемыми (по умолчанию это не так):источник
x
в лямбде остается внутри лямбды, то есть лямбда-функция может изменять только свою собственную копиюx
. Изменение не видно снаружи, оригиналx
остается неизменным. Учтите, что лямбды реализованы как классы функторов; захваченные переменные соответствуют переменным-членам.mutable
Ключевое слово является способом проколотьconst
завесу вы задрапировать над вашими объектами. Если у вас есть константная ссылка или указатель на объект, вы не можете изменять этот объект каким-либо образом, кроме как и когда он помеченmutable
.С вашей
const
ссылкой или указателем вы обязаны:const
.mutable
Исключение делает это так , вы можете написать или набор элементов данных, отмеченыmutable
. Это единственное внешне видимое отличие.Внутренне те
const
методы, которые видны вам, также могут писать в элементы данных, которые помеченыmutable
. По существу, констатная вуаль пронизана всесторонне. Разработчик API должен полностью убедиться, чтоmutable
он не разрушаетconst
концепцию и используется только в особых случаях.mutable
Ключевое слово помогает , потому что это ясно элементы марки данных, которые подпадают под действие этих особых случаев.На практике вы можете использовать
const
навязчиво всю свою кодовую базу (вы, по сути, хотите «заразить» свою кодовую базуconst
«болезнью»). В этом мире указатели и ссылкиconst
за очень немногими исключениями дают код, который легче рассуждать и понимать. Для интересного отступления ищите «ссылочную прозрачность».Без
mutable
ключевого слова вы в конечном итоге будете вынуждены использовать егоconst_cast
для обработки различных полезных особых случаев, которые он позволяет (кэширование, подсчет ссылок, отладка данных и т. Д.). К сожалению,const_cast
это значительно более разрушительно, чемmutable
потому, что заставляет клиента API разрушатьconst
защиту объектов, которые он использует. Кроме того, это вызывает широкоеconst
разрушение:const_cast
использование константного указателя или ссылки позволяет беспрепятственную запись и вызов метода для доступа к видимым элементам. По сравнениюmutable
разработчик API должен осуществлять точный контроль надconst
исключениями, и обычно эти исключения скрыты вconst
методах, работающих с частными данными.(NB. Я несколько раз обращался к видимости данных и методов . Я говорю о членах, помеченных как открытые или закрытые или защищенные, что является совершенно другим типом защиты объектов, обсуждаемым здесь .)
источник
const_cast
для изменения частиconst
объекта приводит к неопределенному поведению.const_cast
для реализации мутации переменных-членов вconst
методе, вы бы не попросили клиента выполнить приведение - вы бы сделали это внутри метода с помощьюconst_cast
ingthis
. По сути, это позволяет вам обходить constness для произвольных участников на определенном сайте вызовов , в то времяmutable
как вы можете удалять const для конкретного участника на всех сайтах вызовов. Последнее обычно то, что вы хотите для типичного использования (кэширование, статистика), но иногда const_cast соответствует шаблону.const_cast
Модель делает посадку лучше в некоторых случаях, например, когда вы хотите , чтобы временно изменить элемент, а затем восстановить его (очень похожеboost::mutex
). Метод логически постоянен, поскольку конечное состояние совпадает с исходным, но вы хотите сделать это временное изменение.const_cast
может быть полезен, потому что он позволяет вам исключить const конкретно в этом методе, если выmutable
не отмените мутацию, но это не будет целесообразно, так как это удалит защиту const из всех методов, которые не обязательно следуют за "do" Отменить "шаблон.const_cast
возможной бомбу замедленного действия.mutable
не имеет такой проблемы, так как такие объекты не могут быть помещены в постоянную память.Ваше использование с boost :: mutex - именно то, для чего предназначено это ключевое слово. Другое использование для внутреннего кэширования результатов для ускорения доступа.
По сути, 'mutable' применяется к любому атрибуту класса, который не влияет на внешне видимое состояние объекта.
В примере кода на ваш вопрос изменяемый может быть неуместным, если значение done_ влияет на внешнее состояние, это зависит от того, что находится в ...; часть.
источник
Mutable предназначен для маркировки конкретного атрибута как модифицируемого изнутри
const
методов. Это его единственная цель. Тщательно подумайте, прежде чем использовать его, потому что ваш код, вероятно, будет чище и более читабельным, если вы измените дизайн, а не будете его использоватьmutable
.http://www.highprogrammer.com/alan/rants/mutable.html
Примеры, которые приводит автор, включают в себя кэширование и временные отладочные переменные.
источник
mutable
может сделать код более читабельным и понятным. В следующем примере значениеread
может быть таким,const
как ожидалось. `mutable m_mutex; Контейнер m_container; void add (Item item) {Замок блокировки (m_mutex); m_container.pushback (пункт); } Элемент read () const {Lockguard lock (m_mutex); return m_container.first (); } `Это полезно в ситуациях, когда у вас есть скрытое внутреннее состояние, такое как кеш. Например:
И тогда вы можете заставить
const HashTable
объект все еще использовать егоlookup()
метод, который модифицирует внутренний кеш.источник
mutable
существует, если вы сделаете вывод, что можно изменять данные в другой постоянной функции.Намерение состоит в том, что у вас может быть функция, которая «ничего не делает» с внутренним состоянием объекта, и поэтому вы помечаете функцию
const
, но вам действительно может понадобиться изменить состояние некоторых объектов таким образом, чтобы это не влияло на его правильность. функциональность.Ключевое слово может служить подсказкой для компилятора - теоретический компилятор может поместить постоянный объект (например, глобальный) в память, которая была помечена только для чтения. Наличие
mutable
намеков на то, что этого делать не следует.Вот несколько веских причин объявлять и использовать изменяемые данные:
mutable boost::mutex
вполне разумно.источник
const
нее (и такая проверка будет успешной или неудачной независимо отconst
илиmutable
).const
Недостаточно просто объявить функцию :const
функция может иметь побочные эффекты, такие как изменение глобальной переменной или что-то, переданное в функцию, так что это бесполезная гарантия для этого доказательства.const
ключевым словом в C ++.Ну, да, это то, что он делает. Я использую его для членов, которые модифицируются методами, которые не изменяют логически состояние класса, например, для ускорения поиска путем реализации кэша:
Теперь вы должны использовать это с осторожностью - проблемы параллелизма являются большой проблемой, так как вызывающая сторона может предположить, что они потокобезопасны, если используют только
const
методы. И, конечно же, изменениеmutable
данных не должно каким-либо существенным образом изменять поведение объекта, что может быть нарушено приведенным мною примером, если, например, ожидалось, что изменения, записанные на диск, будут немедленно видны приложению. ,источник
Mutable используется, когда у вас есть переменная внутри класса, которая используется только внутри этого класса для сигнализации таких вещей, как, например, мьютекс или блокировка. Эта переменная не меняет поведение класса, но необходима для обеспечения безопасности потока самого класса. Таким образом, если бы не «изменяемый», вы не смогли бы иметь «константные» функции, потому что эту переменную нужно будет изменить во всех функциях, доступных внешнему миру. Поэтому, mutable был введен для того, чтобы сделать переменную-член доступной для записи даже функцией const.
источник
mutable в основном используется в деталях реализации класса. Пользователь класса не должен знать об этом, поэтому метод, который он считает «должен» быть постоянным, может быть. Ваш пример того, что mutex должен быть изменяемым, является хорошим каноническим примером.
источник
Его использование не является хаком, хотя, как и многие другие вещи в C ++, изменяемый может быть хаком для ленивого программиста, который не хочет идти назад и помечать что-то, что не должно быть константным, как неконстантное.
источник
Используйте «mutable», когда для вещей, которые ЛОГИЧЕСКИ не сохраняют состояния для пользователя (и, следовательно, должны иметь «const» геттеры в API-интерфейсах открытого класса), но НЕ являются безлимитными в базовой реализации (код в вашем .cpp).
Случаи, которые я использую чаще всего, - это ленивая инициализация "простых старых данных" без состояния. А именно, он идеален в узких случаях, когда такие элементы дороги либо для сборки (процессор), либо для переноса (память), и многие пользователи объекта никогда не будут запрашивать их. В этой ситуации вы хотите ленивую конструкцию на стороне сервера для повышения производительности, поскольку 90% созданных объектов вообще не нужно будет создавать их, но вам все равно нужно представить правильный API без сохранения состояния для общего пользования.
источник
Изменяемое меняет значение
const
с битового константа на логический констант для класса.Это означает, что классы с изменяемыми членами больше не являются побитовыми const и больше не будут появляться в секциях исполняемого файла, доступных только для чтения.
Кроме того, он изменяет проверку типов, позволяя
const
функциям-членам изменять изменяемые элементы без использованияconst_cast
.См. Другие ответы для более подробной информации, но я хотел бы подчеркнуть, что это не просто для безопасности типов и что это влияет на скомпилированный результат.
источник
В некоторых случаях (например, плохо спроектированные итераторы) класс должен хранить счетчик или другое случайное значение, которое не влияет на основное «состояние» класса. Это чаще всего, где я вижу изменчивое использование. Без изменчивости вы были бы вынуждены пожертвовать всем постоянством вашего дизайна.
Мне кажется, что большую часть времени я чувствую это как хак. Полезно в очень немногих ситуациях.
источник
Классический пример (как упоминалось в других ответах) и единственная ситуация, в которой я видел
mutable
ключевое слово, использовавшееся до сих пор, - это кэширование результата сложногоGet
метода, где кеш реализован как элемент данных класса, а не как статическая переменная в методе (по причинам разделения между несколькими функциями или простой чистоты).Как правило, альтернативой использованию
mutable
ключевого слова обычно является статическая переменная в методе илиconst_cast
приеме.Другое подробное объяснение здесь .
источник
const_cast
только тогда, когда вы знаете (или были гарантированы), что что-то не изменится (например, при взаимодействии с библиотеками C) или когда вы знаете, что это не было объявлено как const. То есть, изменение константной переменной const приводит к неопределенному поведению.const_cast
может быть использован для изменения члена класса вconst
методе, о чем я и говорил ...const_cast
, как сказано, это допускается только тогда, когда объект не был объявленconst
. Напримерconst Frob f; f.something();
,void something() const { const_cast<int&>(m_foo) = 2;
приводит к неопределенному поведению.Изменяемый может быть полезен, когда вы переопределяете виртуальную функцию const и хотите изменить переменную члена дочернего класса в этой функции. В большинстве случаев вы не захотите изменять интерфейс базового класса, поэтому вы должны использовать собственную переменную-член.
источник
Ключевое слово mutable очень полезно при создании заглушек для целей тестирования класса. Вы можете заглушить функцию const и при этом иметь возможность увеличивать (изменяемые) счетчики или любую тестовую функциональность, которую вы добавили в свою заглушку. Это сохраняет интерфейс класса заглушки нетронутым.
источник
Один из лучших примеров, где мы используем mutable - это глубокая копия. в конструкторе копирования мы отправляем в
const &obj
качестве аргумента. Таким образом, новый созданный объект будет иметь постоянный тип. Если мы хотим изменить (в основном мы не будем изменять, в редких случаях мы можем изменить) элементы этого вновь созданного объекта const, нам нужно объявить его какmutable
.mutable
класс хранения может использоваться только для не статических неконстантных данных-членов класса. Изменяемый член данных класса может быть изменен, даже если он является частью объекта, который объявлен как const.В приведенном выше примере мы можем изменить значение переменной-члена,
x
хотя она является частью объекта, который объявлен как const. Это потому, что переменнаяx
объявлена как изменяемая. Но если вы попытаетесь изменить значение переменной-членаy
, компилятор выдаст ошибку.источник
Само ключевое слово 'mutable' на самом деле является зарезервированным ключевым словом. Часто оно используется для изменения значения постоянной переменной. Если вы хотите иметь несколько значений constsnt, используйте ключевое слово mutable.
источник