C++基本功:全面掌握const、volatile和mutable關(guān)鍵字
 
CONST
一.一般應(yīng)用
1.const修飾各種變量的用法.
   a.取代define
     #define D_INT 100
     #define D_LONG 100.29
     ………
     const int D_INT = 100;
     const D_INT = 100;     //如果定義的int類型,可省略int.
     const long D_LONG = 100.29;
     ………
     const int& a = 100; 
     const替代define雖然增加分配空間,可它卻保證了類型安全.
在C標(biāo)準(zhǔn)中,const定義的數(shù)據(jù)相當(dāng)于全局的,而C++中視聲明的位置而定.
   b.修飾指針相關(guān)的變量
以三組簡(jiǎn)單的定義示意:
     Group1:   
     int a = 0;    
     const int* b = &a;------------  [1]                
     int const *b = &a;------------  [2]                     
     const int* const b = &a;---- [4]   
     Group2:  
     const char *p = "const";--------------[1] 
     char const *p = "const";--------------[2]   
     char* const p = "const";--------------[3]   
     const char * const p = "const";----[4]      
     Group3:
      int a=0;
        const int &b = a;---------------[1]
     int const &b = a;---------------[2]  
     int & const b = a;--------------[3]  //--->修飾引用時(shí),const被忽略
     const int & const b = a;-----[4]
總結(jié):
     1.如果const位于星號(hào)左側(cè),則const用來修飾指針?biāo)赶虻淖兞?
即指針指向的為不可變的.
     2.如果const位于星號(hào)右側(cè),const就是修飾指針本身,即指針本身是
不可變的.
因此,[1]和[2]的情況相同,指針?biāo)赶騼?nèi)容不可變(const放在變量
聲明符的位置無關(guān)),
這種情況下不允許對(duì)內(nèi)容進(jìn)行更改,如不能*a = 3 ;
     3.[3]中指針本身是不可變的,而指針?biāo)赶虻膬?nèi)容是可變的,這種情況
下不能對(duì)指針本身
進(jìn)行更改操作,如a++是錯(cuò)誤的
     4.[4]中指針本身和指向的內(nèi)容均為常量.(引用特殊:引用在使用增加
遇義時(shí),增加它代表的變量.所以qualifiers on reference are ignoredv.
延伸點(diǎn): 
注意示例:
       1.const int& reference = 1000; 
       2.char* p = "const"
         char*& q ;
2.const在函數(shù)環(huán)境下的各種應(yīng)用
常用法示例如下:
   const A&  _Fun(const  A& _in);  //修飾引用型傳入?yún)?shù)
   // A  _Fun(const A& _in);
   //A& _Fun(const A& _in);
   //上面的兩種,在函數(shù)內(nèi)部有特殊的步驟,這里不詳提了…..
   const  A*  _Fun( const  A* _in);   //修飾指針型傳入?yún)?shù)
   void _Fun( ) const;   //修飾class成員函數(shù)
   const  A&  _Fun(A& _in );  //修飾返回值
   const A & operator(const A& _in);  //同時(shí)修飾傳入?yún)?shù)和返回值
   a.修飾參數(shù)
如void _Fun(const A* _in)或 void _Fun(const A& _in);
它們被修飾后,在函數(shù)執(zhí)行期間行為特性同于上面的講解,
注意:這不會(huì)改變?cè)瓉頂?shù)據(jù)的是否是const的屬性.
   b.修飾函數(shù)返回值
    const A&  _Fun( )
    const A*   _Fun( );
注意:由于生命期不同步的問題,不可將局部的變量的指針或引用返回(static除外).
另外,傳出來的視情況,代表不同的意思…
對(duì)于A&返回類型,你若將之賦與其它變量,那么它實(shí)際執(zhí)行的是將返回的變量
    (或引用)代表的數(shù)據(jù)賦出..而你若將其它值賦予之,那么被賦予的是變量或引
用代表的數(shù)據(jù). 而const A& 一般是防止之做為左值被賦值.
這個(gè)地方還有很多的細(xì)節(jié)問題(譬如在連續(xù)賦值、返回的臨時(shí)對(duì)象的處理、
重載的const和非cosnt運(yùn)算符等等),讀者自己在實(shí)踐中需要多多總結(jié).
使用可變(mutable)成員隱藏實(shí)現(xiàn)細(xì)節(jié)
作者: Builder.com
鍵字 mutable 是一個(gè)奇怪的修飾符(specifier),它只能夠用于一個(gè)類的非靜態(tài)數(shù)據(jù)成員。下面我將討論 mutable 的語義和用法,但是首先我要解釋一下 C++ 對(duì)象模型的一個(gè)關(guān)鍵概念。
對(duì)象的狀態(tài)
一個(gè)對(duì)象的狀態(tài)由其非靜態(tài)數(shù)據(jù)成員的值構(gòu)成,因此,修改一個(gè)數(shù)據(jù)成員將會(huì)改變整個(gè)對(duì)象的狀態(tài)。將一個(gè)成員函數(shù)聲明為 const 能夠保證它不會(huì)改變對(duì)象的狀態(tài)。
然而在一些情況下,對(duì)象的邏輯狀態(tài)與基物理狀態(tài)之間可能有差別。例如,對(duì)于一個(gè)表示繪畫圖像的對(duì)象就存在這種情況。如果圖像還沒有更改,那
么我們就認(rèn)為其狀態(tài)沒有發(fā)生變化。然而,從底層實(shí)現(xiàn)方面來說,如果大對(duì)象在一段時(shí)間沒有活動(dòng),那么它們的內(nèi)存通常會(huì)被交換到一個(gè)文件中。交換一個(gè)圖像并不
會(huì)真地影響其狀態(tài),但是對(duì)象的一些數(shù)據(jù)成員可能會(huì)發(fā)生變化,在這里可能會(huì)發(fā)生變化的是指針、標(biāo)志位等。
在用戶調(diào)用一個(gè)諸如 Redraw() 之類的 const 
成員函數(shù)時(shí),他們并不關(guān)心這個(gè)函數(shù)在內(nèi)部是如何實(shí)現(xiàn)的。從他們的角度來說,這個(gè)函數(shù)并不改變對(duì)象的邏輯狀態(tài),因此被聲明為 const。Redraw()
 有可能修改對(duì)象的物理狀態(tài)這一事實(shí)是一個(gè)他們不應(yīng)該關(guān)心的實(shí)現(xiàn)細(xì)節(jié)。例如:
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
}
class Image
可變(mutable)數(shù)據(jù)成員
如果嘗試編譯這段代碼,你會(huì)得到一個(gè)編譯錯(cuò)誤。雖然 Redraw() 聲明為 const,但是它修改了一個(gè)數(shù)據(jù)成員。解決這個(gè)編譯錯(cuò)誤的方法是將 isLoaded 聲明為一個(gè) mutable 數(shù)據(jù)成員:
class Image
{
public:
 int Redraw() const;
 //..
private:
 mutable bool isLoaded;//can be changed by a const function
};
不像普通的數(shù)據(jù)成員,const 成員函數(shù)可以修改 mutable 數(shù)據(jù)成員。
Mutable 數(shù)據(jù)成員的使用看上去像是騙術(shù),因?yàn)樗軌蚴?const 函數(shù)修改對(duì)象的數(shù)據(jù)成員。然而,明智地使用 mutable 關(guān)鍵字可以提高代碼質(zhì)量,因?yàn)樗軌蜃屇阆蛴脩綦[藏實(shí)現(xiàn)細(xì)節(jié),而無須使用不確定的東西,比如 const_cast<>。
volatile關(guān)鍵字
  volatile是c/c++中一個(gè)鮮為人知的關(guān)鍵字,該關(guān)鍵字告訴編譯器不要持有變量的臨時(shí)拷貝,它可以適用于基礎(chǔ)類型
  如:int,char,long......也適用于C的結(jié)構(gòu)和C++的類。當(dāng)對(duì)結(jié)構(gòu)或者類對(duì)象使用volatile修飾的時(shí)候,結(jié)構(gòu)或者
  類的所有成員都會(huì)被視為volatile.
  使用volatile并不會(huì)否定對(duì)CRITICAL_SECTION,Mutex,Event等同步對(duì)象的需要
  例如:
  int i;
  i = i + 3;
  無論如何,總是會(huì)有一小段時(shí)間,i會(huì)被放在一個(gè)寄存器中,因?yàn)樗阈g(shù)運(yùn)算只能在寄存器中進(jìn)行。一般來說,volatitle
  關(guān)鍵字適用于行與行之間,而不是放在行內(nèi)。
  我們先來實(shí)現(xiàn)一個(gè)簡(jiǎn)單的函數(shù),來觀察一下由編譯器產(chǎn)生出來的匯編代碼中的不足之處,并觀察volatile關(guān)鍵字如何修正
  這個(gè)不足之處。在這個(gè)函數(shù)體內(nèi)存在一個(gè)busy loop(所謂busy loop也叫做busy waits,是一種高度浪費(fèi)CPU時(shí)間的循環(huán)方法)
 void getKey(char* pch)
  {
  while (*pch == 0)
  ;
  }
  當(dāng)你在VC開發(fā)環(huán)境中將最優(yōu)化選項(xiàng)都關(guān)閉之后,編譯這個(gè)程序,將獲得以下結(jié)果(匯編代碼)
  ;       while (*pch == 0)
  $L27
  ; Load the address stored in pch
  mov eax, DWORD PTR _pch$[ebp]
  ; Load the character into the EAX register
  movsx eax, BYTE PTR [eax]
  ; Compare the value to zero
  test eax, eax
  ; If not zero, exit loop
  jne $L28
  ;
  jmp $L27
  $L28
  ;}
  這段沒有優(yōu)化的代碼不斷的載入適當(dāng)?shù)牡刂?,載入地址中的內(nèi)容,測(cè)試結(jié)果。效率相當(dāng)?shù)牡停墙Y(jié)果非常準(zhǔn)確
  現(xiàn)在我們?cè)賮砜纯磳⒕幾g器的所有最優(yōu)化選項(xiàng)開關(guān)都打開以后,重新編譯程序,生成的匯編代碼,和上面的代碼
  比較一下有什么不同
  ;{ 
  ; Load the address stored in pch
  mov eax, DWORD PTR _pch$[esp-4]
  ; Load the character into the AL register
  movsx al, BYTE PTR [eax]
  ; while (*pch == 0)
  ; Compare the value in the AL register to zero
  test al, al
  ; If still zero, try again
  je SHORT $L84
  ;
  ;}