qualifier - mutable c++




除了允許變量被const函數修改外,'mutable'關鍵字還有其他用途嗎? (12)

前一段時間,我遇到了一些用mutable關鍵字標記類的成員變量的代碼。 就我所見,它只是允許你在const方法中修改一個變量:

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

這是這個關鍵字的唯一用處,還是比它更符合眼睛? 我已經在一個類中使用了這種技術,將一個boost::mutex標記為mutable,允許const函數為了線程安全的原因鎖定它,但是,老實說,這感覺就像是一種黑客攻擊。


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

有關更多詳細信息,請參閱其他答案,但我想強調一下,它不僅僅適用於類型安全,並且會影響編譯結果。


mutable確實存在,因為您推斷允許修改其他常數函數中的數據。

目的是你可能有一個函數對對象的內部狀態“什麼也不做”,所以你標記了函數const ,但你可能確實需要修改一些對象的狀態,而不會影響它的狀態正確的功能。

關鍵字可以作為編譯器的提示 - 理論編譯器可以將一個常量對象(如全局對象)放在內存中,該對象標記為只讀。 mutable提示的存在,這不應該完成。

這裡有一些有效的理由來聲明和使用可變數據:

  • 線程安全。 聲明一個mutable boost::mutex是完全合理的。
  • 統計。 計算給定函數的一些或全部參數的調用次數。
  • 記憶化。 計算一些昂貴的答案,然後將其存儲起來供將來參考,而不是再次重新計算。

你使用boost :: mutex正是這個關鍵字的意圖。 另一個用途是內部結果緩存來加速訪問。

基本上,'可變'適用於任何不影響對像外部可見狀態的類屬性。

在你的問題中的示例代碼中,如果done_的值影響外部狀態,mutable可能不適用,它取決於...中的內容; 部分。


你對它的使用並不是黑客行為,儘管像C ++中的許多事情一樣,mutable 可以對懶惰的程序員進行破解,而懶惰的程序員不想一路走下去,並且標記不應該是非const的東西。


可變是將特定屬性標記為可以在const方法中修改。 這是它唯一的目的。 在使用它之前請仔細考慮,因為如果您更改設計而不是使用mutable ,您的代碼可能會更乾淨,更具可讀性。

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

所以如果上面的瘋狂不是可變的,它是什麼? 這裡有一個微妙的例子:mutable用於一個對像在邏輯上是常量的情況,但實際上需要改變。 這些案例很少,但存在很多。

作者給出的例子包括緩存和臨時調試變量。


在某些情況下(如設計不佳的迭代器),類需要保留一個計數或一些其他附帶值,這並不影響該類的主要“狀態”。 這是我經常看到使用可變的地方。 如果沒有變化,你將被迫犧牲設計的整體性。

大部分時間對我來說,這感覺就像是一場黑客攻擊。 在極少數情況下很有用。


它允許區分const和邏輯const。 邏輯常量是指對像不會通過公共接口可見的方式更改,如鎖定示例。 另一個例子是一個類,它在第一次請求時計算一個值,並緩存結果。

由於c ++ 11 mutable可以在lambda表達式中使用,表示通過值捕獲的東西是可修改的(它們不是默認的):

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

對於邏輯上無狀態的用戶使用“mutable”(因此在公共類的API中應該有“const”getters),但在底層IMPLEMENTATION(.cpp中的代碼)中不是無狀態的。

我最經常使用它的情況是無狀態“普通舊數據”成員的惰性初始化。 也就是說,對於構建(處理器)或隨身攜帶(內存)這樣的成員而言,這種成員的成本很高,並且該對象的許多用戶永遠不會要求這些成員,這種情況非常理想。 在這種情況下,你需要在性能的後端進行懶惰的構建,因為90%的對像根本不需要構建它們,但仍然需要為公共消費呈現正確的無狀態API。


為類測試目的創建存根時,mutable關鍵字非常有用。 你可以存根const函數,並且仍然能夠增加(可變)計數器或者你添加到存根的任何測試功能。 這保持了存根類的接口完好無損。


當你重寫一個const虛函數並且想要在該函數中修改你的子類成員變量時,mutable會很方便。 在大多數情況下,你不想改變基類的接口,所以你必須使用你自己的可變成員變量。


經典的例子(如其他答案中提到的)以及我目前看到的關於mutable關鍵字的唯一情況是用於緩存複雜Get方法的結果,其中緩存是作為類的數據成員實現的,而不是作為該方法中的靜態變量(出於幾個功能之間的共享或普通清潔的原因)。

通常,使用mutable關鍵字的替代方法通常是方法或const_cast技巧中的靜態變量。

另一個詳細的解釋在here


那麼,是的,這就是它的作用。 我將它用於由不會在邏輯上改變類的狀態的方法修改的成員 - 例如,通過實現緩存來加速查找:

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