天天看點

C++中的關鍵字剖析(整理)

(一)volatile

volatile的作用是: 作為指令 關鍵字,確定本條指令不會因 編譯器的優化而省略,且要求每次直接讀值. 簡單地說就是防止編譯器對代碼進行優化.比如如下程式:

1 2 3 4

XBYTE[2]=0x55;

XBYTE[2]=0x56;

XBYTE[2]=0x57;

XBYTE[2]=0x58;

對外部硬體而言,上述四條語句分别表示不同的操作,會産生四種不同的動作,但是編譯器卻會對上述四條語句進行優化,認為隻有XBYTE[2]=0x58(即忽略前三條語句,隻産生一條機器代碼)。如果鍵入 volatile,則編譯器會逐一的進行編譯并産生相應的機器代碼(産生四條代碼). 一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣, 編譯器就不會去假設這個變量的值了。精确地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用儲存在 寄存器裡的備份。下面是volatile變量的幾個例子: 1). 并行裝置的硬體寄存器(如:狀态寄存器) 2). 一個中斷服務子程式中會通路到的非自動變量(Non-automatic variables) 3). 多線程應用中被幾個任務共享的變量 這是區分C程式員和 嵌入式系統程式員的最基本的問題:嵌入式系統程式員經常同硬體、中斷、RTOS等等打交道,所有這些都要求使用volatile變量。不懂得volatile内容将會帶來災難。 假設被面試者正确地回答了這是問題(嗯,懷疑是否會是這樣),我将稍微深究一下,看一下這家夥是不是真正懂得volatile完全的重要性。 1). 一個參數既可以是const還可以是volatile嗎?解釋為什麼。 2). 一個指針可以是volatile 嗎?解釋為什麼。 3). 下面的函數被用來計算某個整數的平方,它能實作預期設計目标嗎?如果不能,試回答存在什麼問題:

1 2 3 4

int

square(

volatile

int

*ptr)

{

return

((*ptr) * (*ptr));

}

下面是答案: 1). 是的。一個例子是隻讀的 狀态寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程式不應該試圖去修改它。 2). 是的。盡管這并不很常見。一個例子是當一個中斷服務子程式修改一個指向一個buffer的 指針時。 3). 這段代碼是個惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個volatile型參數, 編譯器将産生類似下面的代碼:

1 2 3 4 5 6 7

int

square(

volatile

int

* &ptr)

//這裡參數應該申明為引用,不然函數體裡隻會使用副本,外部沒法更改

{

int

a,b;

a = *ptr;

b = *ptr;

return

a*b;

}

由于*ptr的值可能在兩次取值語句之間發生改變,是以a和b可能是不同的。結果,這段代碼可能傳回的不是你所期望的平方值!正确的代碼如下:

1 2 3 4 5 6

long

square(

volatile

int

*ptr)

{

int

a;

a = *ptr;

return

a*a;

}

(二)asm asm是C++中的一個關鍵字,用于在C++源碼中内嵌彙編語言。

__asm關鍵字啟動内聯彙編并且能寫在任何C++合法語句之處。它不能單獨出現,必須接彙編指令、一組被大括号包含的指令或一對空括号。術語“__asm 塊”在這裡是任意一個指令或一組指令無論是否在括号内。 [3]  

C++中的關鍵字剖析(整理)

Visual Studio 2015起始頁 以下代碼片段是在括号内的一個簡單的__asm塊。 _asm { mov al, 2  mov dx, 0xD007 out al, dx } (三)typeid typeid用于傳回指針或引用所指對象的實際類型

C++的typeid

編輯 注意:typeid是操作符,不是函數! 運作時獲知變量類型名稱,可以使用 typeid(變量).name(),需要注意不是所有編譯器都輸出"int"、"float"等之類的名稱,對于這類的編譯器可以這樣使用:float f = 1.1f; if( typeid(f) == typeid(0.0f) ) …… 示例代碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

#include <iostream>

#include <typeinfo>

using

namespace

std;

int

main(

void

)

{

// sample 1 

cout << 

typeid

(1.1f).name() << endl;  

// sample 2 

class

Base1 {}; 

class

Derive1:

public

Base1 {}; 

Derive1 d1; 

Base1& b1 = d1; 

cout << 

typeid

(b1).name() << endl; 

// 輸出"class Base1",因為Derive1和Base1之間沒有多态性 

// sample 3, 編譯時需要加參數 /GR 

class

Base2 {  

virtual

void

fun( 

void

) {} 

}; 

class

Derive2:

public

Base2 { }; 

Derive2 d2; 

Base2& b2 = d2; 

cout << 

typeid

(b2).name() << endl; 

// 輸出"class Derive2",因為Derive1和Base1之間有了多态性 

// sample 4 

class

Derive22:

public

Base2 { }; 

// 指針強制轉化失敗後可以比較指針是否為零,而引用卻沒辦法,是以引用制轉化失敗後抛出異常 

Derive2* pb1 = 

dynamic_cast

<Derive2*>(&b2); 

cout << boolalpha << (0!=pb1) << endl; 

// 輸出"true",因為b2本身确實是指向Derive2 

Derive22* pb2 = 

dynamic_cast

<Derive22*>(&b2);

cout << boolalpha << (0!=pb2) << endl; 

// 輸出"false",因為b2本身不是指向Derive2 

try

{  

Derive2& rb1 = 

dynamic_cast

<Derive2&>(b2);  

cout << 

"true"

<< endl; 

}

catch

( bad_cast ) { 

cout << 

"false"

<< endl; } 

try

{  Derive22& rb2 = 

dynamic_cast

<Derive22&>(b2);  cout << 

"true"

<< endl; } 

catch

( ... ) 

// 應該是 bad_cast,但不知道為什麼在VC++6.0中卻不行?因為VC++6.0預設狀态是禁用 RTTI 的,啟用方式:project->setting->c/c++->category->c++ Language 下面第二個複選框選中。 

{  cout << 

"false"

<< endl; } 

return

0;}

(四)mutable mutable 可以用來指出,即使成員函數或者類變量為const,其某個成員也可以被修改。 在c++的類中, 如果一個成員函數被const 修飾,那麼它将無法修改其成員變量的,但是如果這個成員變量是被mutable修飾的話,則可以修改。 (五)explicit explicit 構造函數的作用 解析: explicit構造函數是用來防止隐式轉換的。請看下面的代碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

class

Test1

{

public

:

Test1(

int

n)

{

num=n;

}

//普通構造函數

private

:

int

num;

};

class

Test2

{

public

:

explicit

Test2(

int

n)

{

num=n;

}

//explicit(顯式)構造函數

private

:

int

num;

};

int

main()

{

Test1 t1=12;

//隐式調用其構造函數,成功

Test2 t2=12;

//編譯錯誤,不能隐式調用其構造函數

Test2 t2(12);

//顯式調用成功

return

0;

}

Test1的 構造函數帶一個int型的參數,代碼23行會隐式轉換成調用Test1的這個構造函數。而Test2的構造函數被聲明為explicit(顯式),這表示不能通過隐式轉換來調用這個構造函數,是以代碼24行會出現編譯錯誤。 普通構造函數能夠被 隐式調用。而explicit構造函數隻能被顯式調用。