深入分析C 中幾個最不常用的關鍵字

2022-10-04 05:18:06 字數 3939 閱讀 8013

mutable關鍵字

關鍵字mutable是中乙個不常用的關鍵字,他只能用於類的非靜態和非常量資料成員

我們知道乙個物件的狀態由該物件的非靜態資料成員決定,所以隨著資料成員的改變,

對像的狀態也會隨之發生變化!

如果乙個類的成員函式被宣告為const型別,表示該函式不會改變物件的狀態,也就是

該函式不會修改類的非靜態資料成員.但是有些時候需要在該類函式中對類的資料成員

進行賦值.這個時候就需要用到mutable關鍵字了

例如:複製** **如下:

class demo

~demo(){}

public:

bool getflag() const

private:

int  m_naccess;

bool m_bflag;

};int main()

編譯上面的**會出現 error c2166: l-value specifies const object的錯誤

說明在const型別的函式中改變了類的非靜態資料成員.

這個時候需要使用mutable來修飾一下要在const成員函式中改變的非靜態資料成員

m_naccess,**如下:

複製** **如下:

class demo

~demo(){}

public:

&程式設計客棧nbsp;   bool getflag() const

private:

mutable int  m_naccess;

bool m_bflag;

};int main()

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

volatile關鍵字

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

如:int,char,long......也適用於c的結構和c++的類。當對結構或者類物件使用volatile修飾的時候,結構或者

類的所有成員都會被視為volatile.

使用volatile並不會否定對critical_section,mutex,event等同步物件的需要

例如:int i;

i = i + 3;

無論如何,總是會有一小段時間,i會被放在乙個暫存器中,因為算術運算只能在暫存器中進行。一般來說,volatitle

關鍵字適用於行與行之間,而不是放在行內。

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

複製** **如下:

void getkey(char* pch)

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

複製** **如下:

;  程式設計客棧;     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

;}這段沒有優化的**不斷的載入適當的位址,載入位址中的內容,測試結果。效率相當的低,但是結果非常準確

現在我們再來看看將編譯器的所有最優化選項開關都開啟以後,重新編譯程式,生成的彙編**,和上面的**

比較一下有什麼不同

複製** **如下:

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

複製** **如下:

void getkey(volatile char* pch)

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

複製** **如下:

;這次的修改結果比較完美,位址不會改變,所以位址宣告被移動到迴圈之外。位址內容是volatile,所以每迴圈之中它不斷的被重新檢查。

把乙個const volatile變數作為引數傳遞給函式是合法的。如此的宣告意味著函式不能改變變數的值,但是變數的值卻可以被另乙個執行緒在任何時間改變掉。

explicit關鍵字

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

例如:你可以宣告這樣乙個建構函式

複製** **如下:

class array

;在這裡explicit關鍵字起著至關重要的作用,如果沒有這個關鍵字的話,這個建構函式有能力將int轉換成array.一旦這種

情況發生,你可以給array支派乙個整數值而不會引起任何的問題,比如:

array arr;

...arr = 40;

此時,c++的自動型別轉換會把40轉換成擁有40個元素的array,並且指派給arr變數,這個結果根本就不是我們想要的結果.如果

我們將建構函式宣告為explicit,上面的賦值操作就會導致編譯器報錯,使我們可以及時發現錯誤.

需要注意的是:explicit同樣也能阻止"以賦值語法進行帶有轉型操作的初始化";

例如:複製** **如下:

array arr(40);//正確

array arr = 40;//錯誤

看一下以下兩種操作:

x x;

y y(x);//顯式型別轉換

另一種x x;

y y = x;//隱式型別轉換

這兩種操作存在乙個小小的差別,第一種方式式通過顯式型別轉換,根據型別x產生了型別y的新物件;第二種方式通過隱式轉換產生了乙個型別y的新物件.

explicit關鍵字的應用主要就是上面所說的建構函式定義種,參考該關鍵字的應用可以看看stl源**,其中大量使用了該關鍵字

__based關鍵字

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

舉個例子:

複製** **如下:

typedef struct tagdemostruct demostruct, * pdemostruct;

handle hfilemapping = createfilemapping(...);

lpvoid lpshare = (lpdword)mapviewoffile(...);

demostruct __based(lpshare)* lpdemo;

上面的例子宣告了乙個指標lpdemo,內部儲存的是從lpshare開始的偏移值,也就是lphead是以lpshare為基準的偏移值.

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

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

在這裡我只是介紹了幾個並不時很常見的關鍵字的意義即用法,其他那些常見的關鍵字介紹他們的文章已經不少了在這裡

就不再一一介紹了.希望這些內容能對大家有一定的幫助!

本文標題: 深入分析c++中幾個最不常用的關鍵字

本文位址:

C 幾個不常用的關鍵字

asm 插入乙個彙編指令.auto 宣告乙個本地變數.const cast 從乙個const變數中丟擲.dynamic cast 動態投射.explicit 僅用在構造器的正確匹配.extern 告訴編譯器在別的地方變數已經被定義過了.friend 允許非函式成員使用私有資料.inline 定義乙個...

C 中幾個比較不常用的關鍵字

mutable關鍵字 關鍵字mutable是c 中乙個不常用的關鍵字,他只能用於類的非靜態和非常量資料成員 我們知道乙個物件的狀態由該物件的非靜態資料成員決定,所以隨著資料成員的改變,對像的狀態也會隨之發生變化 如果乙個類的成員函式被宣告為const型別,表示該函式不會改變物件的狀態,也就是 該函式...

C 中幾個比較不常用的關鍵字

關鍵字mutable是c 中乙個不常用的關鍵字,他只能用於類的非靜態和非常量資料成員 我們知道乙個物件的狀態由該物件的非靜態資料成員決定,所以隨著資料成員的改變,對像的狀態也會隨之發生變化 如果乙個類的成員函式被宣告為const型別,表示該函式不會改變物件的狀態,也就是 該函式不會修改類的非靜態資料...