C++中mutable、volatile关键字
C++中mutable、volatile關(guān)鍵字
mutable和volatile
?????? 很少遇到這兩個(gè)關(guān)鍵字,學(xué)嵌入式估計(jì)知道后者,深入研究C++的估計(jì)知道前者。
???????(1)mutable
?????? 在C++中,mutable是為了突破const的限制而設(shè)置的。被mutable修飾的變量,將永遠(yuǎn)處于可變的狀態(tài),即使在一個(gè)const函數(shù)中,甚至結(jié)構(gòu)體變量或者類對(duì)象為const,其mutable成員也可以被修改。
[cpp]?view plaincopy print????????mutable在類中只能夠修飾非靜態(tài)數(shù)據(jù)成員。mutable 數(shù)據(jù)成員的使用看上去像是騙術(shù),因?yàn)樗軌蚴筩onst函數(shù)修改對(duì)象的數(shù)據(jù)成員。然而,明智地使用 mutable 關(guān)鍵字可以提高代碼質(zhì)量,因?yàn)樗軌蜃屇阆蛴脩綦[藏實(shí)現(xiàn)細(xì)節(jié),而無(wú)須使用不確定的東西。我們知道,如果類的成員函數(shù)不會(huì)改變對(duì)象的狀態(tài),那么這個(gè)成員函數(shù)一般會(huì)聲明成const的。但是,有些時(shí)候,我們需要在const的函數(shù)里面修改一些跟類狀態(tài)無(wú)關(guān)的數(shù)據(jù)成員,那么這個(gè)數(shù)據(jù)成員就應(yīng)該被mutalbe來(lái)修飾。
[cpp]?view plaincopy print??
???????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ì)稱的定義,一個(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è)問題:
???????1、為什么要保護(hù)類的成員變量不被修改?
?????? 2、為什么用const保護(hù)了成員變量,還要再定義一個(gè)mutable關(guān)鍵字來(lái)突破const的封鎖線?
?????? 3、到底有沒有必要使用const 和 mutable這兩個(gè)關(guān)鍵字?
?????? 保護(hù)類的成員變量不在成員函數(shù)中被修改,是為了保證模型的邏輯正確,通過用const關(guān)鍵字來(lái)避免在函數(shù)中錯(cuò)誤的修改了類對(duì)象的狀態(tài)。并且在所有使用該成員函數(shù)的地方都可以更準(zhǔn)確的預(yù)測(cè)到使用該成員函數(shù)的帶來(lái)的影響。而mutable則是為了能突破const的封鎖線,讓類的一些次要的或者是輔助性的成員變量隨時(shí)可以被更改。沒有使用const和mutable關(guān)鍵字當(dāng)然沒有錯(cuò),const和mutable關(guān)鍵字只是給了建模工具更多的設(shè)計(jì)約束和設(shè)計(jì)靈活性,而且程序員也可以把更多的邏輯檢查問題交給編譯器和建模工具去做,從而減輕程序員的負(fù)擔(dān)。
???????(2)volatile
?????? 象const一樣,volatile是一個(gè)類型修飾符。volatile修飾的數(shù)據(jù),編譯器不可對(duì)其進(jìn)行執(zhí)行期寄存于寄存器的優(yōu)化。這種特性,是為了滿足多線程同步、中斷、硬件編程等特殊需要。遇到這個(gè)關(guān)鍵字聲明的變量,編譯器對(duì)訪問該變量的代碼就不再進(jìn)行優(yōu)化,從而可以提供對(duì)特殊地址的直接訪問。
?????? volatile原意是“易變的”,但這種解釋簡(jiǎn)直有點(diǎn)誤導(dǎo)人,應(yīng)該解釋為“直接存取原始內(nèi)存地址”比較合適。“易變”是相對(duì)與普通變量而言其值存在編譯器(優(yōu)化功能)未知的改變情況(即不是通過執(zhí)行代碼賦值改變其值的情況),而是因外在因素引起的,如多線程,中斷等。編譯器進(jìn)行優(yōu)化時(shí),它有時(shí)會(huì)取一些值的時(shí)候,直接從寄存器里進(jìn)行存取,而不是從內(nèi)存中獲取,這種優(yōu)化在單線程的程序中沒有問題,但到了多線程程序中,由于多個(gè)線程是并發(fā)運(yùn)行的,就有可能一個(gè)線程把某個(gè)公共的變量已經(jīng)改變了,這時(shí)其余線程中寄存器的值已經(jīng)過時(shí),但這個(gè)線程本身還不知道,以為沒有改變,仍從寄存器里獲取,就導(dǎo)致程序運(yùn)行會(huì)出現(xiàn)未定義的行為。并不是因?yàn)橛胿olatile修飾了的變量就是“易變”了,假如沒有外因,即使用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ì)它的讀寫都可能有不同意義;
???????使用該關(guān)鍵字的例子如下:
[cpp]?view plaincopy print??
?????? volatile 指出 i是隨時(shí)可能發(fā)生變化的,每次使用它的時(shí)候必須從i的地址中讀取,因而編譯器生成的匯編代碼會(huì)重新從i的地址讀取數(shù)據(jù)放在b中。而優(yōu)化做法是,由于編譯器發(fā)現(xiàn)兩次從i讀數(shù)據(jù)的代碼之間的代碼沒有對(duì)i進(jìn)行過操作,它會(huì)自動(dòng)把上次讀的數(shù)據(jù)(即10)放在b中,而不是重新從i里面讀。這樣以來(lái),如果i是一個(gè)寄存器變量或者表示一個(gè)端口數(shù)據(jù)就容易出錯(cuò),所以說(shuō)volatile可以保證對(duì)特殊地址的直接訪問。
[html]?view plaincopy print??????? 如果上述兩條語(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ú)條件真。?
?????? 3、對(duì)volatile變量的讀寫不會(huì)被優(yōu)化掉。如果你對(duì)一個(gè)變量賦值但后面沒用到,編譯器常常可以省略那個(gè)賦值操作,然而對(duì)Memory Mapped IO的處理是不能這樣優(yōu)化的。
總結(jié)
以上是生活随笔為你收集整理的C++中mutable、volatile关键字的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ 在继承中虚函数、纯虚函数、普通函
- 下一篇: C++静态成员函数小结