Подтвердить что ты не робот

Имеет ли ключевое слово "изменчивое" какие-либо цели, кроме возможности изменять переменную функцией const?

Некоторое время назад я наткнулся на некоторый код, который помечал членную переменную класса с ключевым словом mutable. Насколько я вижу, это просто позволяет вам изменить переменную в методе const:

class Foo  
{  
private:  
    mutable bool done_;  
public:  
    void doSomething() const { ...; done_ = true; }  
};

Является ли это единственным использованием этого ключевого слова или для него больше, чем для глаз? С тех пор я использовал этот метод в классе, отмечая boost::mutex как изменяемый, позволяя функциям const блокировать его для причин безопасности потоков, но, честно говоря, это похоже на хак.

4b9b3361

Ответ 1

Он позволяет дифференцировать побитовые константы и логические константы. Логическая константа - это когда объект не изменяется таким образом, который видим через открытый интерфейс, например, ваш пример блокировки. Другим примером может служить класс, который вычисляет значение при первом запросе и кэширует результат.

Так как С++ 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

Ответ 2

Ключевое слово mutable - это способ прошивки завесы const, которую вы накладываете на свои объекты. Если у вас есть ссылка на 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 с указателем или ссылкой const позволяет неограниченную запись и метод, вызывающий доступ к видимым членам. Напротив, mutable требует, чтобы дизайнер API выполнял мелкозернистый контроль над исключениями const, и обычно эти исключения скрыты в методах const, работающих с частными данными.

(NB Я несколько раз ссылаюсь на видимость данных и методов. Я говорю о членах, отмеченных как public vs. private или protected, который является совершенно другим типом защиты объектов, обсуждаемым .)

Ответ 3

Ваше использование с boost:: mutex - это именно то, для чего предназначено это ключевое слово. Другое использование - для внутреннего кэширования результатов для быстрого доступа.

В принципе, "mutable" применяется к любому атрибуту класса, который не влияет на внешнее видимое состояние объекта.

В примере кода в вашем вопросе изменяемый может быть неуместным, если значение done_ влияет на внешнее состояние, оно зависит от того, что находится в...; часть.

Ответ 4

Mutable предназначен для обозначения определенного атрибута как модифицируемого из методов const. Это единственная его цель. Подумайте внимательно, прежде чем использовать его, потому что ваш код, вероятно, будет более чистым и более читаемым, если вы измените дизайн, а не используйте mutable.

http://www.highprogrammer.com/alan/rants/mutable.html

Итак, если вышеупомянутое безумие не то, что mutable для, для чего это нужно? Вот тонкий случай: изменчивый для случай, когда объект логически постоянной, но на практике изменение. Эти случаи мало и далеко между ними, но они существуют.

Примеры, которые дает автор, включают кэширование и временные отладочные переменные.

Ответ 5

Это полезно в ситуациях, когда у вас есть скрытое внутреннее состояние, такое как кеш. Например:

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(), который изменяет внутренний кеш.

Ответ 6

Хорошо, да, это то, что он делает. Я использую его для членов, которые модифицируются методами, которые не логически изменяют состояние класса - например, для ускорения поиска путем реализации кеша:

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;
};

Теперь вы должны использовать это с осторожностью - проблемы с проблемой concurrency вызывают серьезную озабоченность, поскольку вызывающий может предположить, что они являются потокобезопасными, если использовать только методы const. И, конечно, модификация данных mutable не должна изменять поведение объекта каким-либо значительным образом, что может быть нарушено в примере, который я дал, если бы, например, ожидалось, что изменения, записанные на диск, будут немедленно видны к приложению.

Ответ 7

mutable существует, поскольку вы делаете вывод, чтобы можно было изменять данные в постоянной константе.

Цель состоит в том, что у вас может быть функция, которая "ничего не делает" во внутреннее состояние объекта, и поэтому вы отмечаете функцию const, но вам действительно может потребоваться изменить некоторые состояния объектов способами, которые не влияют на его правильную функциональность.

Ключевое слово может выступать в качестве подсказки для компилятора - теоретический компилятор может помещать постоянный объект (такой как глобальный) в память, который был помечен только для чтения. Присутствие mutable означает, что этого не следует делать.

Вот некоторые веские причины для объявления и использования изменяемых данных:

  • Безопасность резьбы. Объявление a mutable boost::mutex вполне разумно.
  • Статистика. Подсчет количества вызовов функции, учитывая некоторые или все его аргументы.
  • запоминание. Вычисляя некоторый дорогой ответ, а затем сохраняя его для будущей ссылки, а не повторно вычисляя его.

Ответ 8

Mutable используется, когда у вас есть переменная внутри класса, которая используется только в этом классе, чтобы сигнализировать о таких вещах, как, например, мьютекс или блокировка. Эта переменная не изменяет поведение класса, но необходима для обеспечения безопасности потока самого класса. Таким образом, если без "изменчивого", вы не сможете иметь функции "const", потому что эту переменную необходимо будет изменить во всех функциях, доступных для внешнего мира. Таким образом, измененный был введен для того, чтобы сделать переменную-переменную переменной доступной даже функцией const.

Указанный mutable сообщает компилятору и читателю, что он является безопасным и ожидается, что переменная-член может быть изменена в пределах const член.

Ответ 9

mutable в основном используется для детализации реализации класса. Пользователь класса не должен знать об этом, поэтому метод, который, по его мнению, "должен" быть const, может быть. Ваш пример использования mutute beuteable является хорошим каноническим примером.

Ответ 10

Ваше использование этого не является взломом, хотя, как и многие другие вещи на С++, mutable может быть взломан для ленивого программиста, который не хочет возвращаться назад и отмечать что-то, что не должно быть const, -const.

Ответ 11

Используйте "mutable", когда для вещей, которые LOGICALLY не имеют статуса для пользователя (и, следовательно, должны иметь "const" getters в API открытого класса), но не являются апатридами в базовом ОСУЩЕСТВЛЕНИИ (код в вашем .cpp).

Случаями, которые я использую чаще всего, является ленивая инициализация нечетких "простых старых данных". А именно, он идеален в узких случаях, когда такие члены дороги для сборки (процессора) или переноса (памяти), и многие пользователи объекта никогда не будут их запрашивать. В этой ситуации вам нужна ленивая конструкция на заднем конце для производительности, поскольку 90% построенных объектов никогда не понадобится для их создания вообще, но вам все равно нужно представить правильный API без учета состояния для общественного потребления.

Ответ 12

В некоторых случаях (например, плохо спроектированные итераторы) класс должен поддерживать счет или другое случайное значение, что на самом деле не влияет на основное "состояние" этого класса. Это чаще всего там, где я вижу mutable. Без изменчивости вы будете вынуждены жертвовать всей консистенцией своего дизайна.

Мне кажется, что это очень тяжело. Полезно в очень немногих ситуациях.

Ответ 13

Классический пример (как упоминалось в других ответах), и единственная ситуация, с которой я встречал ключевое слово mutable, используемое до сих пор, заключается в кэшировании результата сложного метода Get, где кеш реализован как член данных класса, а не как статическая переменная в методе (из-за совместного использования нескольких функций или простой чистоты).

В общем, альтернативы использованию ключевого слова mutable обычно представляют собой статическую переменную в методе или трюк const_cast.

Еще одно подробное объяснение в здесь.

Ответ 14

Ключевое слово mutable очень полезно при создании заглушек для целей тестирования класса. Вы можете заглушить функцию const и по-прежнему сможете увеличивать (изменяемые) счетчики или любую другую функциональность теста, которую вы добавили в свою заглушку. Это препятствует сохранению интерфейса заглушенного класса.

Ответ 15

Mutable изменяет значение 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.
}

См. другие ответы для получения дополнительной информации, но я хотел бы подчеркнуть, что это не просто для типа-saftey и что оно влияет на скомпилированный результат.

Ответ 16

Переменная может быть удобной, когда вы переопределяете виртуальную функцию const и хотите изменить переменную члена дочернего класса в этой функции. В большинстве случаев вы не хотите изменять интерфейс базового класса, поэтому вам нужно использовать переменную переменную-члена.

Ответ 17

Самое ключевое слово "mutable" на самом деле является зарезервированным ключевым словом. Часто используется для изменения значения постоянной переменной. Если вы хотите иметь несколько значений constsnt, используйте ключевое слово mutable.

//Prototype 
class tag_name{
                :
                :
                mutable var_name;
                :
                :
               };   

Ответ 18

Один из лучших примеров использования 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, компилятор вызовет ошибку.