[轉]關鍵字 mutable的中文意思是:“可變的、易變的”與constant(即C++中 const)是反義詞。C++中的mutable是為了突破const限制而設定的,被mutable修飾的變數將永遠處於可變狀態,即使是位於 const修飾的函數中。mutable又是一個奇怪的修飾符(specifier),它只能夠用於一個類的非待用資料成員。下面我將討論 mutable 的語義和用法,但是首先我要解釋一下 C++ 物件模型的一個關鍵概念。
對象的狀態
一個對象的狀態由其非待用資料成員的值構成,因此,修改一個資料成員將會改變整個對象的狀態。將一個成員函式宣告為 const 能夠保證它不會改變對象的狀態。
然而在一些情況下,對象的邏輯狀態與其物理狀態之間可能有差別。例如,對於一個表示繪畫映像的對象就存在這種情況。如果映像還沒有更改,那麼我們就認為其狀態沒有發生變化。然而,從底層實現方面來說,如果大對象在一段時間沒有活動,那麼它們的記憶體通常會被交換到一個檔案中。交換一個映像並不會真地影響其狀態,但是對象的一些資料成員可能會發生變化,在這裡可能會發生變化的是指標、標誌位等。
在使用者調用一個諸如 Redraw() 之類的 const 成員函數時,他們並不關心這個函數在內部是如何?的。從他們的角度來說,這個函數並不改變對象的邏輯狀態,因此被聲明為 const。Redraw() 有可能修改對象的物理狀態這一事實是一個他們不應該關心的實現細節。例如:
int Image::Redraw() const
{
if (isLoaded==false)
{
//..read image data from a disk into a local buffer
isLoaded=true; //changing a data member's value
}
//..paint image in the screen
}
可變(mutable)資料成員
如果嘗試編譯這段代碼,你會得到一個編譯錯誤。雖然 Redraw() 聲明為 const,但是它修改了一個資料成員。解決這個編譯錯誤的方法是將 isLoaded 聲明為一個 mutable 資料成員:
class Image {
public:
int Redraw() const;
//..
private:
mutable bool isLoaded;//can be changed by a const function
};
不像普通的資料成員,const 成員函數可以修改 mutable 資料成員。
Mutable 資料成員的使用看上去像是騙術,因為它能夠使 const 函數修改對象的資料成員。然而,明智地使用 mutable 關鍵字可以提高代碼品質,因為它能夠讓你向使用者隱藏實現細節,而無須使用不確定的東西,比如 const_cast<>。
mutalbe的中文意思是“可變的,易變的”,跟constant(既C++中的const)是反義詞。
在C++中,mutable也是為了突破const的限制而設定的。被mutable修飾的變數,將永遠處於可變的狀態,即使在一個const函數中。
我們知道,如果類的成員函數不會改變對象的狀態,那麼這個成員函數一般會聲明成const的。但是,有些時候,我們需要在const的函數裡面修改一些跟類狀態無關的資料成員,那麼這個資料成員就應該被mutalbe來修飾。
下面是一個小例子:
class ClxTest
{
public:
void Output() const;
};
void ClxTest::Output() const
{
cout << "Output for test!" << endl;
}
void OutputTest(const ClxTest& lx)
{
lx.Output();
}
類ClxTest的成員函數Output是用來輸出的,不會修改類的狀態,所以被聲明為const的。
函數OutputTest也是用來輸出的,裡面調用了對象lx的Output輸出方法,為了防止在函數中調用其他成員函數修改任何成員變數,所以參數也被const修飾。
如果現在,我們要增添一個功能:計算每個對象的輸出次數。如果用來計數的變數是普通的變數的話,那麼在const成員函數Output裡面是不能修改該變數的值的;而該變數跟對象的狀態無關,所以應該為了修改該變數而去掉Output的const屬性。這個時候,就該我們的mutable出場了 ——只要用mutalbe來修飾這個變數,所有問題就迎刃而解了。
下面是修改過的代碼:
class ClxTest
{
public:
ClxTest();
~ClxTest();
void Output() const;
int GetOutputTimes() const;
private:
mutable int m_iTimes;
};
ClxTest::ClxTest()
{
m_iTimes = 0;
}
ClxTest::~ClxTest()
{}
void ClxTest::Output() const
{
cout << "Output for test!" << endl;
m_iTimes++;
}
int ClxTest::GetOutputTimes() const
{
return m_iTimes;
}
void OutputTest(const ClxTest& lx)
{
cout << lx.GetOutputTimes() << endl;
lx.Output();
cout << lx.GetOutputTimes() << endl;
}
計數器m_iTimes被mutable修飾,那麼它就可以突破const的限制,在被const修飾的函數裡面也能被修改