C++要點(1)mutable關鍵字
mutable語義
在C++中,mutable是為了突破const的限制而設定的。
被mutable修飾的變數,將永遠處於可變的狀態,即使在一個const函式中,甚至結構體變數或者類物件為const,其mutable成員也可以被修改。
例如:
#include <iostream>
class Tester
{
public:
Tester(int x, int y) : a_normal(x),b_mutable(y)
{}
void printAll() const
{
std ::cout << "a_normal=" << a_normal << "; b_mutable=" << b_mutable << std::endl;
}
int a_normal;
mutable int b_mutable;
};
int main()
{
const Tester tst(2,3);
tst.a_normal = 22;
tst.b_mutable = 33;
tst.printAll();
return 0;
}
編譯報錯,報錯資訊如下
test4mutable.cpp: In function ‘int main()’ :test4mutable.cpp:20: error: assignment of data-member ‘Tester::a_normal’ in read-only structure
我們將報錯的那行程式碼註釋掉後重新編譯,通過執行。
mutable在類中只能修飾非靜態資料成員。
const語義
const修飾的變數在任何情況下它的值都不會被改變。
const修飾的函式不能直接或間接地改變任何函式體以外的變數的值,即使呼叫一個可能造成這種改變的函式也不行。
關於const關鍵字的詳細解析請參考博文:const關鍵字淺析
mutable和const的關係
mutable修飾的變數將永遠處於可變的狀態,即使在一個const函式中。
所以mutable和const形成了一個對稱的定義:一個永遠不變,一個永遠可變。
那麼有3個問題需要考慮:
- 為什麼要保護類的成員變數不被修改?
- 為什麼mutable要突破const的封鎖?
- 使用const和mutable關鍵字的必要性。
保護類的成員變數不在成員函式中被修改,是為了保證模型的邏輯正確,通過用const關鍵字來避免在函式中錯誤的修改了類物件的狀態。並且在所有使用該成員函式的地方都可以更準確的預測到使用該成員函式的帶來的影響。
而mutable則是為了能突破const的封鎖線,讓類的一些次要的或者是輔助性的成員變數隨時可以被更改。
沒有使用const和mutable關鍵字當然沒有錯,const和mutable關鍵字只是給了建模工具更多的設計約束和設計靈活性,而且程式設計師也可以把更多的邏輯檢查問題交給編譯器和建模工具去做,從而減輕程式設計師的負擔。
mutable提供的靈活性
如果類的成員函式不會改變物件的狀態,那麼這個成員函式一般會宣告成const的。但是,如果希望在const函式中修改一些和類狀態無關的資料成員,那麼可以使用mutable修飾這個成員。
明智地使用mutable關鍵字,可以向用戶隱藏實現細節,而無需使用不確定的東西,提高程式碼質量。
例如:
#include <iostream>
class Tester
{
public:
Tester(int x, int y) : a_normal(x),b_mutable(y), printCount(0)
{}
void printAll() const
{
std::cout << "a_normal=" << a_normal << "; b_mutable=" << b_mutable << std::endl;
std::cout << "print " << ++printCount<< " times" << std::endl << std::endl;
}
int a_normal;
mutable int b_mutable;
mutable int printCount;
};
int main()
{
const Tester tst(2,3);
tst.b_mutable = 33;
tst.printAll();
tst.printAll();
tst.printAll();
return 0;
}
在上例中,printAll函式是一個const函式,但是希望在列印的同時,記錄列印次數,因此,在類中增加了一個成員變數printCount,該成員變數和類本身的狀態無關,並且需要在printAll函式中改變,因此使用mutable關鍵字修飾,實現了統計列印次數的目的。
執行結果如下圖所示: