1. 程式人生 > 其它 >【專業技術】C++裡面重要的幾個關鍵字的用法

【專業技術】C++裡面重要的幾個關鍵字的用法

編者按:

這幾個關鍵字非常重要,程式中經常見到他們的身影,但是確切意思有時候還需要多搜尋下才能知道。筆者這裡把它搬出來,也是希望大家引起重視,努力掌握它。

C++關鍵字:mutable、volatile、explicit以及__based

mutable關鍵字

關鍵字mutable是C++中一個不常用的關鍵字,他只能用於類的非靜態和非常量資料成員我們知道一個物件的狀態由該物件的非靜態資料成員決定,所以隨著資料成員的改變,對像的狀態也會隨之發生變化!

如果一個類的成員函式被宣告為const型別,表示該函式不會改變物件的狀態,也就是該函式不會修改類的非靜態資料成員.但是有些時候需要在該類函式中對類的資料成員進行賦值.這個時候就需要用到mutable關鍵字了

例如:

1class Demo
2  {
3  public:
4         Demo(){}
5         ~Demo(){}
6  public:
7         bool getFlag() const
8         {
9                m_nAccess++;
10              return m_bFlag;
11        }
12 private:
13      int  m_nAccess;
14      bool m_bFlag;
15 };
16
17 int main()
18 {
19      return 0;
20 }
21

編譯上面的程式碼會出現 error C2166: l-value specifies const object的錯誤說明在const型別的函式中改變了類的非靜態資料成員.這個時候需要使用mutable來修飾一下要在const成員函式中改變的非靜態資料成員

m_nAccess,程式碼如下:

1  class Demo
2  {
3  public:
4         Demo(){}
5         ~Demo(){}
6  public:
7         bool getFlag() const
8         {
9                m_nAccess++;
10              return m_bFlag;
11       }
12 private:
13        mutable int  m_nAccess;
14        bool m_bFlag;
15 };
16
17 int main()
18 {
19       return 0;
20 }
21

這樣再重新編譯的時候就不會出現錯誤了!

volatile關鍵字

volatile是c/c++中一個鮮為人知的關鍵字,該關鍵字告訴編譯器不要持有變數的臨時拷貝,它可以適用於基礎型別

如:int,char,long......也適用於C的結構和C++的類。當對結構或者類物件使用volatile修飾的時候,結構或者類的所有成員都會被視為volatile.使用volatile並不會否定對CRITICAL_SECTION,Mutex,Event等同步物件的需要

例如:

1int i; 2i = i + 3;

無論如何,總是會有一小段時間,i會被放在一個暫存器中,因為算術運算只能在暫存器中進行。一般來說,volatitle關鍵字適用於行與行之間,而不是放在行內。

我們先來實現一個簡單的函式,來觀察一下由編譯器產生出來的彙編程式碼中的不足之處,並觀察volatile關鍵字如何修正這個不足之處。在這個函式體記憶體在一個busy loop(所謂busy loop也叫做busy waits,是一種高度浪費CPU時間的迴圈方法)

1void getKey(char* pch) 2 { 3 while (*pch == 0); 4 }

當你在VC開發環境中將最優化選項都關閉之後,編譯這個程式,將獲得以下結果(彙編程式碼)

1while (*pch == 0)
2$L27
3 ;Load the address stored in pch
4 mov eax, DWORD PTR _pch$[ebp]
5 ; Load the character into the EAX register
6 movsx eax, BYTE PTR [eax]
7 ; Compare the value to zero
8 test eax, eax
9 ; If not zero, exit loop
10 jne $L28
11 ;
12 jmp $L27
13$L28
14;}

這段沒有優化的程式碼不斷的載入適當的地址,載入地址中的內容,測試結果。效率相當的低,但是結果非常準確現在我們再來看看將編譯器的所有最優化選項開關都開啟以後,重新編譯程式,生成的彙編程式碼,和上面的程式碼

比較一下有什麼不同


1  ;{ 
2  ; Load the address stored in pch
3  mov eax, DWORD PTR _pch$[esp-4]
4  ; Load the character into the AL register
5  movsx al, BYTE PTR [eax]
6  ; while (*pch == 0)
7  ; Compare the value in the AL register to zero
8  test al, al
9  ; If still zero, try again
10 je SHORT $L84
11 ;
12 ;}

從程式碼的長度就可以看出來,比沒有優化的情況要短的多。需要注意的是編譯器把MOV指令放到了迴圈之外。這在單執行緒中是一個非常好的優化,但是,在多執行緒應用程式中,如果另一個執行緒改變了變數的值,則迴圈永遠不會結束。被測試的值永遠被放在暫存器中,所以該段程式碼在多執行緒的情況下,存在一個巨大的BUG。解決方法是重新

寫一次getKey函式,並把引數pch宣告為volatile,程式碼如下:

1void getKey(volatile char* pch)
2   {
3         while (*pch == 0) ;
4   }

這次的修改對於非最優化的版本沒有任何影響,下面請看最優化後的結果:

1  ;{
2  ; Load the address stored in pch
3  mov eax, DWORD PTR _pch$[esp-4]
4  ;  while (*pch == 0)
5  $L84:
6  ; Directly compare the value to zero
7  cmp BYTE PTR [eax], 0
8  ; If still zero, try again
9  je SHORT $L84
10 ;
11 ;}

這次的修改結果比較完美,地址不會改變,所以地址宣告被移動到迴圈之外。地址內容是volatile,所以每次迴圈之中它不斷的被重新檢查。把一個const volatile變數作為引數傳遞給函式是合法的。如此的宣告意味著函式不能改變變數的值,但是變數的值卻可以被另一個執行緒在任何時間改變掉。

explicit關鍵字

我們在編寫應用程式的時候explicit關鍵字基本上是很少使用,它的作用是"禁止單引數建構函式"被用於自動型別轉換,其中比較典型的例子就是容器型別,在這種型別的建構函式中你可以將初始長度作為引數傳遞給建構函式.

例如:

你可以宣告這樣一個建構函式

1class Array
2  {
3  public:
4         explicit Array(int size);
5   
6  };

在這裡explicit關鍵字起著至關重要的作用,如果沒有這個關鍵字的話,這個建構函式有能力將int轉換成Array.一旦這種情況發生,你可以給Array支派一個整數值而不會引起任何的問題,比如:

1Array arr;
2
3arr = 40;

此時,C++的自動型別轉換會把40轉換成擁有40個元素的Array,並且指派給arr變數,這個結果根本就不是我們想要的結果.如果我們將建構函式宣告為explicit,上面的賦值操作就會導致編譯器報錯,使我們可以及時發現錯誤.需要注意的是:explicit同樣也能阻止"以賦值語法進行帶有轉型操作的初始化";

1Array arr(40);//正確
2Array arr = 40;//錯誤

看一下以下兩種操作:

1X x;
2Y y(x);//顯式型別轉換

另一種

1X x;
2Y y = x;//隱式型別轉換

這兩種操作存在一個小小的差別,第一種方式式通過顯式型別轉換,根據型別x產生了型別Y的新物件;第二種方式通過隱式轉換產生了一個型別Y的新物件.explicit關鍵字的應用主要就是上面所說的建構函式定義種,參考該關鍵字的應用可以看看STL原始碼,其中大量使用了該關鍵字

__based關鍵字 該關鍵字主要用來解決一些和共享記憶體有關的問題,它允許指標被定義為從某一點開始算的32位偏移值,而不是記憶體種的絕對位置

舉個例子:


1 typedef struct tagDEMOSTRUCT {
2 int a;
3 char sz[10];
4 } DEMOSTRUCT, * PDEMOSTRUCT;
5
6HANDLE hFileMapping = CreateFileMapping();
7LPVOID lpShare = (LPDWORD)MapViewOfFile();
8
9DEMOSTRUCT __based(lpShare)* lpDemo;

上面的例子聲明瞭一個指標lpDemo,內部儲存的是從lpShare開始的偏移值,也就是lpHead是以lpShare為基準的偏移值.

上面的例子種的DEMOSTRUCT只是隨便定義的一個結構,用來代表任意的結構.

雖然__based指標使用起來非常容易,但是,你必須在效率上付出一定的代價.每當你用__based指標處理資料,CPU都必須為它加上基地址,才能指向真正的位置.

轉自:http://www.cppblog.com/leetaolion/archive/2008/04/09/46586.html