mutable、volatile的使用
本文轉(zhuǎn)載自http://blog.csdn.net/tht2009/article/details/6920511
???????(1)mutable
?????? 在C++中,mutable是為了突破const的限制而設(shè)置的。被mutable修飾的變量,將永遠(yuǎn)處于可變的狀態(tài),即使在一個(gè)const函數(shù)中,甚至結(jié)構(gòu)體變量或者類(lèi)對(duì)象為const,其mutable成員也可以被修改。
struct ST{int a;mutable int b;};const ST st={1,2};st.a=11;//編譯錯(cuò)誤st.b=22;//允許
 
 
???????mutable在類(lèi)中只能夠修飾非靜態(tài)數(shù)據(jù)成員。mutable 數(shù)據(jù)成員的使用看上去像是騙術(shù),因?yàn)樗軌蚴筩onst函數(shù)修改對(duì)象的數(shù)據(jù)成員。然而,明智地使用 mutable 關(guān)鍵字可以提高代碼質(zhì)量,因?yàn)樗軌蜃屇阆蛴脩?hù)隱藏實(shí)現(xiàn)細(xì)節(jié),而無(wú)須使用不確定的東西。我們知道,如果類(lèi)的成員函數(shù)不會(huì)改變對(duì)象的狀態(tài),那么這個(gè)成員函數(shù)一般會(huì)聲明成const的。但是,有些時(shí)候,我們需要在const的函數(shù)里面修改一些跟類(lèi)狀態(tài)無(wú)關(guān)的數(shù)據(jù)成員,那么這個(gè)數(shù)據(jù)成員就應(yīng)該被mutalbe來(lái)修飾。
class ST{int a;mutable int showCount;void Show()const;…};ST::Show(){…//顯示代碼a=1;//錯(cuò)誤,不能在const成員函數(shù)中修改普通變量showCount++;//正確}
?
???????const承諾的是一旦某個(gè)變量被其修飾,那么只要不使用強(qiáng)制轉(zhuǎn)換(const_cast),在任何情況下該變量的值都不會(huì)被改變,無(wú)論有意還是無(wú)意,而被const修飾的函數(shù)也一樣,一旦某個(gè)函數(shù)被const修飾,那么它便不能直接或間接改變?nèi)魏魏瘮?shù)體以外的變量的值,即使是調(diào)用一個(gè)可能造成這種改變的函數(shù)都不行。這種承諾在語(yǔ)法上也作出嚴(yán)格的保證,任何可能違反這種承諾的行為都會(huì)被編譯器檢查出來(lái)。
?????? mutable的承諾是如果某個(gè)變量被其修飾,那么這個(gè)變量將永遠(yuǎn)處于可變的狀態(tài),即使在一個(gè)const函數(shù)中。這與const形成了一個(gè)對(duì)稱(chēng)的定義,一個(gè)永遠(yuǎn)不變,而另外一個(gè)是永遠(yuǎn)可變。
?????? 看一個(gè)變量或函數(shù)是否應(yīng)該是const,只需看它是否應(yīng)該是constant或invariant,而看一個(gè)變量是否應(yīng)該是mutable,也只需看它是否是forever mutative。
?????? 這里出現(xiàn)了令人糾結(jié)的3個(gè)問(wèn)題:
???????1、為什么要保護(hù)類(lèi)的成員變量不被修改?
?????? 2、為什么用const保護(hù)了成員變量,還要再定義一個(gè)mutable關(guān)鍵字來(lái)突破const的封鎖線(xiàn)?
?????? 3、到底有沒(méi)有必要使用const 和 mutable這兩個(gè)關(guān)鍵字?
?????? 保護(hù)類(lèi)的成員變量不在成員函數(shù)中被修改,是為了保證模型的邏輯正確,通過(guò)用const關(guān)鍵字來(lái)避免在函數(shù)中錯(cuò)誤的修改了類(lèi)對(duì)象的狀態(tài)。并且在所有使用該成員函數(shù)的地方都可以更準(zhǔn)確的預(yù)測(cè)到使用該成員函數(shù)的帶來(lái)的影響。而mutable則是為了能突破const的封鎖線(xiàn),讓類(lèi)的一些次要的或者是輔助性的成員變量隨時(shí)可以被更改。沒(méi)有使用const和mutable關(guān)鍵字當(dāng)然沒(méi)有錯(cuò),const和mutable關(guān)鍵字只是給了建模工具更多的設(shè)計(jì)約束和設(shè)計(jì)靈活性,而且程序員也可以把更多的邏輯檢查問(wèn)題交給編譯器和建模工具去做,從而減輕程序員的負(fù)擔(dān)。
(2)volatile
?????? 象const一樣,volatile是一個(gè)類(lèi)型修飾符。volatile修飾的數(shù)據(jù),編譯器不可對(duì)其進(jìn)行執(zhí)行期寄存于寄存器的優(yōu)化。這種特性,是為了滿(mǎn)足多線(xiàn)程同步、中斷、硬件編程等特殊需要。遇到這個(gè)關(guān)鍵字聲明的變量,編譯器對(duì)訪(fǎng)問(wèn)該變量的代碼就不再進(jìn)行優(yōu)化,從而可以提供對(duì)特殊地址的直接訪(fǎng)問(wèn)。
?????? volatile原意是“易變的”,但這種解釋簡(jiǎn)直有點(diǎn)誤導(dǎo)人,應(yīng)該解釋為“直接存取原始內(nèi)存地址”比較合適。“易變”是相對(duì)與普通變量而言其值存在編譯器(優(yōu)化功能)未知的改變情況(即不是通過(guò)執(zhí)行代碼賦值改變其值的情況),而是因外在因素引起的,如多線(xiàn)程,中斷等。編譯器進(jìn)行優(yōu)化時(shí),它有時(shí)會(huì)取一些值的時(shí)候,直接從寄存器里進(jìn)行存取,而不是從內(nèi)存中獲取,這種優(yōu)化在單線(xiàn)程的程序中沒(méi)有問(wèn)題,但到了多線(xiàn)程程序中,由于多個(gè)線(xiàn)程是并發(fā)運(yùn)行的,就有可能一個(gè)線(xiàn)程把某個(gè)公共的變量已經(jīng)改變了,這時(shí)其余線(xiàn)程中寄存器的值已經(jīng)過(guò)時(shí),但這個(gè)線(xiàn)程本身還不知道,以為沒(méi)有改變,仍從寄存器里獲取,就導(dǎo)致程序運(yùn)行會(huì)出現(xiàn)未定義的行為。并不是因?yàn)橛胿olatile修飾了的變量就是“易變”了,假如沒(méi)有外因,即使用volatile定義,它也不會(huì)變化。而加了volatile修飾的變量,編譯器將不對(duì)其相關(guān)代碼執(zhí)行優(yōu)化,而是生成對(duì)應(yīng)代碼直接存取原始內(nèi)存地址。
???????一般說(shuō)來(lái),volatile用在如下的幾個(gè)地方:
???????1、中斷服務(wù)程序中修改的供其它程序檢測(cè)的變量需要加volatile;
?????? 2、多任務(wù)環(huán)境下各任務(wù)間共享的標(biāo)志應(yīng)該加volatile;
?????? 3、存儲(chǔ)器映射的硬件寄存器通常也要加volatile說(shuō)明,因?yàn)槊看螌?duì)它的讀寫(xiě)都可能有不同意義;
???????使用該關(guān)鍵字的例子如下:
volatile int i=10;int a = i;...//其他代碼,并未明確告訴編譯器,對(duì)i進(jìn)行過(guò)操作int b = i;
?
?????? volatile 指出 i是隨時(shí)可能發(fā)生變化的,每次使用它的時(shí)候必須從i的地址中讀取,因而編譯器生成的匯編代碼會(huì)重新從i的地址讀取數(shù)據(jù)放在b中。而優(yōu)化做法是,由于編譯器發(fā)現(xiàn)兩次從i讀數(shù)據(jù)的代碼之間的代碼沒(méi)有對(duì)i進(jìn)行過(guò)操作,它會(huì)自動(dòng)把上次讀的數(shù)據(jù)(即10)放在b中,而不是重新從i里面讀。這樣以來(lái),如果i是一個(gè)寄存器變量或者表示一個(gè)端口數(shù)據(jù)就容易出錯(cuò),所以說(shuō)volatile可以保證對(duì)特殊地址的直接訪(fǎng)問(wèn)。
//addr為volatile變量 addr=0x57; addr=0x58;
?????? 如果上述兩條語(yǔ)句是對(duì)外部硬件執(zhí)行不同的操作,那么編譯器就不能像對(duì)待普通的程序那樣對(duì)上述語(yǔ)句進(jìn)行優(yōu)化只認(rèn)為“addr=0x58;”而忽略第一條語(yǔ)句(即只產(chǎn)生一條機(jī)器代碼),此時(shí)編譯器會(huì)逐一的進(jìn)行編譯并產(chǎn)生相應(yīng)的機(jī)器代碼(兩條)。
 ???????volatile總是與優(yōu)化有關(guān),編譯器有一種技術(shù)叫做數(shù)據(jù)流分析,分析程序中的變量在哪里賦值、在哪里使用、在哪里失效,分析結(jié)果可以用于常量合并,常量傳播等優(yōu)化,進(jìn)一步可以死代碼消除。但有時(shí)這些優(yōu)化不是程序所需要的,這時(shí)可以用volatile關(guān)鍵字禁止做這些優(yōu)化,它有下面的作用:
   1、不會(huì)在兩個(gè)操作之間把volatile變量緩存在寄存器中。在多任務(wù)、中斷等環(huán)境下,變量可能被其他的程序改變,編譯器自己無(wú)法知道,volatile就是告訴編譯器這種情況。
   2、不做常量合并、常量傳播等優(yōu)化,所以像下面的代碼,if的條件不會(huì)當(dāng)作無(wú)條件真。?
volatile int i = 1; if (i > 0)...
?????? 3、對(duì)volatile變量的讀寫(xiě)不會(huì)被優(yōu)化掉。如果你對(duì)一個(gè)變量賦值但后面沒(méi)用到,編譯器常常可以省略那個(gè)賦值操作,然而對(duì)Memory Mapped IO的處理是不能這樣優(yōu)化的。
?
總結(jié)
以上是生活随笔為你收集整理的mutable、volatile的使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: 【C++深度剖析教程28】C++对象模型
- 下一篇: 【C++深度剖析教程32】new/mal
