C 你知道嗎 之 幾個冷僻的關鍵

2021-08-23 12:52:12 字數 3669 閱讀 3400

mutable關鍵字

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

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

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

如果乙個類的成員函式被宣告為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:

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;

demostruct __based(lpshare)* lpdemo;

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

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

雖然__based指標使用起來非常容易,但是,你必須在效率上付出一定的代價.每當你用__based指標處理資料,cpu都必須

為它加上基位址,才能指向真正的位置.

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

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

不起眼的幾個Ecxel技巧,你知道嗎?

excel是我們很多人都會使用的到的一種工具,其實在excel中有很多是我們不知道的技巧,大家知道嗎?總結幾種不常見的技巧分享給大家。技巧一 一鍵生成臨時選單 不知道大家平時在excel中處理資料的時候,會不會使用臨時選單,其實使用 生成臨時選單 可以完成輸入資料。臨時選單的生成並不難,直接按住 a...

親愛的,你知道嗎?

親愛的,莫怪我,我實在太累了,親愛的,莫怪我,因為我覺得無法學會自私,親愛的,莫怪我,因為我覺得自己心裡太小,小的只能容下我愛你三個字,無法容下任何,因此我把自己丟了,只剩下陪伴你的軀殼,親愛的,因為我喪失了靈魂,所以在你面前如此狼狽,親愛的,因為我自己無法面對現實,所以我哭泣的近似瘋了,親愛的,因...

索引的缺點 你知道嗎?

我們一般都會關心 索引的優點 為什麼要建立 索引 有什麼好處?索引的適應場景,可我沒有注意到 它給我們帶來什麼弊端。接下來總結歸納一下吧。什麼是索引?索引 按我的理解 就是乙個具有標誌性的名稱,相當於一本字典目錄 能根據這個索引 查到自己想到的字。索引的作用 索引分類有哪些?普通索引 普通字段,方便...