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



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



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


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

685   18  

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

    Ничего не найдено.