天天看點

C++ mutable 關鍵字

此篇介紹 C++ 中的 mutable 關鍵字。

類中的 mutable

mutable 從字面意思上來說,是「可變的」之意。

若是要「顧名思義」,那麼這個關鍵詞的含義就有些意思了。顯然,「可變的」隻能用來形容變量,而不可能是「函數」或者「類」本身。然而,既然是「變量」,那麼它本來就是可變的,也沒有必要使用 mutable 來修飾。那麼,mutable 就隻能用來形容某種不變的東西了。

C++ 中,不可變的變量,稱之為常量,使用 const 來修飾。然而,若是 const mutable 聯用,未免讓人摸不着頭腦——到底是可變還是不可變呢?

事實上,mutable 是用來修飾一個 const 示例的部分可變的資料成員的。如果要說得更清晰一點,就是說 mutable 的出現,将 C++ 中的 const 的概念分成了兩種。

二進制層面的 const,也就是「絕對的」常量,在任何情況下都不可修改(除非用 const_cast)。

引入 mutable 之後,C++ 可以有邏輯層面的 const,也就是對一個常量執行個體來說,從外部觀察,它是常量而不可修改;但是内部可以有非常量的狀态。

當然,所謂的「邏輯 const」,在 C++ 标準中并沒有這一稱呼。這隻是為了友善了解,而創造出來的名詞。

顯而易見,mutable 隻能用來修飾類的資料成員;而被 mutable 修飾的資料成員,可以在 const 成員函數中修改。

這裡舉一個例子,展現這類情形。

class HashTable {
 public:
    //...
    std::string lookup(const std::string& key) const
    {
        if (key == last_key_) {
            return last_value_;
        }

        std::string value{this->lookupInternal(key)};

        last_key_   = key;
        last_value_ = value;

        return value;
    }

 private:
    mutable std::string last_key_
    mutable std::string last_value_;
};
           

這裡,我們呈現了一個哈希表的部分實作。顯然,對哈希表的查詢操作,在邏輯上不應該修改哈希表本身。是以,HashTable::lookup 是一個 const 成員函數。在 HashTable::lookup 中,我們使用了 last_key_ 和 last_value_ 實作了一個簡單的「緩存」邏輯。當傳入的 key 與前次查詢的 last_key_ 一緻時,直接傳回 last_value_;否則,則傳回實際查詢得到的 value 并更新 last_key_ 和 last_value_。

在這裡,last_key_ 和 last_value_ 是 HashTable 的資料成員。按照一般的了解,const 成員函數是不允許修改資料成員的。但是,另一方面,last_key_ 和 last_value_ 從邏輯上說,修改它們的值,外部是無有感覺的;是以也就不會破壞邏輯上的 const。為了解決這一沖突,我們用 mutable 來修飾 last_key_ 和 last_value_,以便在 lookup 函數中更新緩存的鍵值。

Lambda 表達式中的 mutable

C++11 引入了 Lambda 表達式,程式員可以憑此建立匿名函數。在 Lambda 表達式的設計中,捕獲變量有幾種方式;其中按值捕獲(Caputre by Value)的方式不允許程式員在 Lambda 函數的函數體中修改捕獲的變量。而以 mutable 修飾 Lambda 函數,則可以打破這種限制。

int x{0};
auto f1 = [=]() mutable {x = 42;};  // okay, 建立了一個函數類型的執行個體
auto f2 = [=]()         {x = 42;};  // error, 不允許修改按值捕獲的外部變量的值
           

需要注意的是,上述 f1 的函數體中,雖然我們給 x 做了指派操作,但是這一操作僅隻在函數内部生效——即,實際是給拷貝至函數内部的 x 進行指派——而外部的 x 的值依舊是 0。

c++

繼續閱讀