關鍵字mutable是C++中一個不常用的關鍵字,他只能用于類的非靜態和非常量數據成員 我們知道一個對象的狀態由該對象的非靜態數據成員決定,所以隨著數據成員的改變, 對像的狀態也會隨之發生變化! 如果一個類的成員函數被聲明為const類型,表示該函數不會改變對象的狀態,也就是 該函數不會修改類的非靜態數據成員.但是有些時候需要在該類函數中對類的數據成員 進行賦值.這個時候就需要用到mutable關鍵字了
例如: class Demo { public: Demo(){} ~Demo(){} public: bool getFlag() const { m_nAccess++; return m_bFlag; } private: int m_nAccess; bool m_bFlag; }; int main() { return 0; }
編譯上面的代碼會出現 error C2166: l-value specifies const object的錯誤 說明在const類型的函數中改變了類的非靜態數據成員.
這個時候需要使用mutable來修飾一下要在const成員函數中改變的非靜態數據成員 m_nAccess,代碼如下:
class Demo { public: Demo(){} ~Demo(){} public: bool getFlag() const { m_nAccess++; return m_bFlag; } private: mutable int m_nAccess; bool m_bFlag; }; int main() { return 0; }
這樣再重新編譯的時候就不會出現錯誤了!
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) { while (*pch == 0) ; }
當你在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 ;}
這段沒有優化的代碼不斷的載入適當的地址,載入地址中的內容,測試結果。效率相當的低,但是結果非常準確
現在我們再來看看將編譯器的所有最優化選項開關都打開以后,重新編譯程序,生成的匯編代碼,和上面的代碼
比較一下有什么不同
;{ ; 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 ; ;}
從代碼的長度就可以看出來,比沒有優化的情況要短的多。需要注意的是編譯器把MOV指令放到了循環之外。這在單線程中是一個非常好的優化,但是,在多線程應用程序中,如果另一個線程改變了變量的值,則循環永遠不會結束。被測試的值永遠被放在寄存器中,所以該段代碼在多線程的情況下,存在一個巨大的BUG。解決方法是重新
寫一次getKey函數,并把參數pch聲明為volatile,代碼如下:
void getKey(volatile char* pch) { while (*pch == 0) ; }
這次的修改對于非最優化的版本沒有任何影響,下面請看最優化后的結果:
;{ ; Load the address stored in pch mov eax, DWORD PTR _pch$[esp-4] ; while (*pch == 0) $L84: ; Directly compare the value to zero cmp BYTE PTR [eax], 0 ; If still zero, try again je SHORT $L84 ; ;}
這次的修改結果比較完美,地址不會改變,所以地址聲明被移動到循環之外。地址內容是volatile,所以每次循環 之中它不斷的被重新檢查。
把一個const volatile變量作為參數傳遞給函數是合法的。如此的聲明意味著函數不能改變變量的值,但是變量的 值卻可以被另一個線程在任何時間改變掉。
explicit關鍵字
我們在編寫應用程序的時候explicit關鍵字基本上是很少使用,它的作用是"禁止單參數構造函數"被用于自動型別轉換,其中比較典型的例子就是容器類型,在這種類型的構造函數中你可以將初始長度作為參數傳遞給構造函數.
例如: 你可以聲明這樣一個構造函數
class Array { public: explicit Array(int size); ...... };
在這里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 { int a; char sz[10]; } DEMOSTRUCT, * PDEMOSTRUCT; HANDLE hFileMapping = CreateFileMapping(...); LPVOID lpShare = (LPDWORD)MapViewOfFile(...);
DEMOSTRUCT __based(lpShare)* lpDemo;
上面的例子聲明了一個指針lpDemo,內部儲存的是從lpShare開始的偏移值,也就是lpHead是以lpShare為基準的偏移值.上面的例子種的DEMOSTRUCT只是隨便定義的一個結構,用來代表任意的結構.
雖然__based指針使用起來非常容易,但是,你必須在效率上付出一定的代價.每當你用__based指針處理數據,CPU都必須為它加上基地址,才能指向真正的位置.
在這里我只是介紹了幾個并不時很常見的關鍵字的意義即用法,其他那些常見的關鍵字介紹他們的文章已經不少了在這里就不再一一介紹了.希望這些內容能對大家有一定的幫助!
在網上看到的介紹性文章,不錯
1. 保留字
C++中,保留字也稱關鍵字,它是預先定義好的標識符。見關鍵字的解釋。
2.關鍵字
C++中已經被系統定義為特殊含義的一類標識符。C++中的關鍵字有:

3.標識符
對變量、函數、標號和其它各種用戶自定義對象的命名。在C++中,標識符長度沒有限制,第一個字符必須是字母或下劃線,其后若有字符則必須為字母、數字或下劃線。例如count2,_x是正確的標識符形式,而hello!,3th則是錯誤的。在C++中標識符區分大小寫,另外標識符不能和C++中的關鍵字相同,也不能和函數同名。
4.聲明
將一個標識符引入一個作用域,此標識符必須指明類型,如果同時指定了它所代表的實體,則聲明也是定義。
5.定義
給所聲明的標識符指定所代表的實體。
6.變量
某個作用域范圍內的命名對象。
7.常量
常量是不接受程序修改的固定值,可以是任意數據類型。可以用后綴準確的描述所期望的常量類型,如浮點類型常量在數字后加F,無符號整型常量加后綴U等等。此外還有串常量如"Please input year:",反斜線字符常量如\n表示回車符。
8. const說明符
const是在變量聲明或函數聲明時所用到的一個修飾符,用它所修飾的實體具有只讀屬性。
9. 輸入
當程序需要執行鍵盤輸入時,可以使用抽取操作付">>"從cin輸入流中抽取字符。如:
int myAge;
cin >> myAge;
10.輸出
當程序需要在屏幕上顯示輸出時,可以使用插入操作符"<<"向cout 輸出流中插入字符。如:
cout << "This is a program. \n ";
11.流
流是既產生信息又消費信息的邏輯設備,通過C++系統和物理設備關聯。C++的I/O系統是通過流操作的。有兩種類型的流:文本流,二進制流。
12.標準輸入輸出庫
它是C++標準庫的組成部分,為C++語言提供了輸入輸出的能力。
13.內置數據類型
由C++直接提供的類型,包括int、float、double、char 、bool、指針、數組和引用。
14.字符類型
包括 char、signed char、unsigned char三種類型。
15.整數類型
包括 short、 int、long 三種類型。
16.long
只能修飾 int , double.
long int 指一種整數類型,它的長度大于等于int型.
long double 指長雙精度類型,長度大于等于double型。
17.short
一種長度少于或等于int型的整數類型。
18.signed
由它所修飾的類型是帶符號的. 只能修飾 int 和 char .
19.布爾型
一種數據類型,其值可為:true, false 兩種。
20.浮點類型
包括float, double , long double 三種類型。其典型特征表現為有尾數或指數。
21.雙精度類型
浮點類型中的一種。在基本數據類型中它是精度最高,表示范圍最大的一種數據類型。
22.void類型
關鍵字之一,指示沒有返回信息。
23.結構類型
類的一種,其成員默認為public型。大多用作無成員函數的數據結構。
24.枚舉類型
一種用戶自定義類型,由用戶定義的值的集合組成。
25.類型轉換
一種數據類型轉換為另一種,包括顯式,隱式兩種方式。
26.指針
一個保存地址或0的對象。
27. 函數指針
每個函數都有地址,指向函數地址的指針稱為函數指針,函數指針指向代碼區中的某個函數,通過函數指針可以調用相應的函數。其定義形式為:
int ( * func ) ( char a, char b);
28.引用
為一個對象或函數提供的另一個名字。
29.鏈表
一種數據結構,由一個個有序的結點組成,每個結點都是相同類型的結構,每個結點都有一個指針成員指向下一個結點。
30.數組
數組是一個由若干同類型變量組成的集合。
31.字符串
標準庫中的一種數據類型,一些常用操作符如+=,==支持其操作。
32.運算符
內置的操作常用符號,例如+,* ,& 等。
33.單目運算符
只能對一個操作數進行操作
34.雙目運算符
可對兩個操作數進行操作
35.三目運算符
可對三個操作數進行操作
36.算術運算符
執行算術操作的運算符,包括:+,-,*,/,%。
37.條件運算符
即"?: " 。
其語法為:
(條件表達式)?(條件為真時的表達式):(條件為假時的表達式)
如:x = a < b ? a : b;
相當于:
if ( a < b)
x = a;
else
x = b;
38.賦值運算符
即:" = "及其擴展賦值運算符
39.左值
能出現在賦值表達式左邊的表達式。
40.右值
能出現在賦值表達式右邊的表達式。
41.運算符的結合性
指表達式中出現同等優先級的操作符時該先做哪個的規定。
42.位運算符
" & "," | " , " ^ "," >> "," << "
43.逗號運算符
即" , "
44.邏輯運算符
" && ", " || " ," ! "
45.關系運算符
">",">=","<=","< "," <= ","== "
46.new運算符
對象創建的操作符。
47.delete運算符
對象釋放操作符,觸發析構函數。
48.內存泄露
操作堆內存時,如果分配了內存,就有責任回收它,否則這塊內存就無法重新使用,稱為內存泄漏。
49.sizeof運算符
獲得對象在內存中的長度,以字節為單位。
50.表達式
由操作符和標識符組合而成,產生一個新的值。
51.算術表達式
用算術運算符和括號將運算對象(也稱操作數)連接起來,符合C++語法規則的式子。
52.關系表達式
用關系運算符和括號將運算對象(也稱操作數)連接起來,符合C++語法規則的式子。
53.邏輯表達式
用邏輯運算符和括號將運算對象(也稱操作數)連接起來,符合C++語法規則的式子。
54.賦值表達式
由賦值運算符將一個變量和一個表達式連接起來,符合C++語法規則的式子。
55.逗號表達式
由逗號操作符將幾個表達式連接起來,符合C++語法規則的式子。
56.條件表達式
由條件運算符將運算對象連接起來,符合C++語法規則的式子。
57.語句
在函數中控制程序流程執行的基本單位,如if語句,while語句,switch語句, do語句, 表達式語句等。
58.復合語句
封閉于大括號{}內的語句序列。
59.循環語句
for 語句, while 語句, do 語句三種。
60.條件語句
基于某一條件在兩個選項中選擇其一的語句稱為條件語句。 61.成員函數
在類中說明的函數稱為成員函數。
62.全局函數
定義在所有類之外的函數。
63.main函數
由系統自動調用開始執行C++程序的第一個函數
64.外部函數
在定義函數時,如果冠以關鍵字extern,表示此函數是外部函數。
65.內聯函數
在函數前加上關鍵字inline說明了一個內聯函數,這使一個函數在程序行里進行代碼擴展而不被調用。這樣的好處是減少了函數調用的開銷,產生較快的執行速度。但是由于重復編碼會產生較長代碼,所以內聯函數通常都非常小。如果一個函數在類說明中定義,則將自動轉換成內聯函數而無需用inline說明。
66.函數重載
在同一作用域范圍內,相同的函數名通過不同的參數類型或參數個數可以定義幾個函數,編譯時編譯器能夠識別實參的個數和類型來決定該調用哪個具體函數。需要注意的是,如果兩個函數僅僅返回類型不同,則編譯時將會出錯,因為返回類型不足以提供足夠的信息以使編譯程序判斷該使用哪個函數。所以函數重載時必須是參數類型或者數量不同。
67.函數覆蓋
對基類中的虛函數,派生類以相同的函數名及參數重新實現之。
68.函數聲明
在C++中,函數聲明就是函數原型,它是一條程序語句,即它必須以分號結束。它有函數返回類型,函數名和參數構成,形式為:
返回類型 function (參數表);
參數表包含所有參數的數據類型,參數之間用逗號分開。如下函數聲明都是合法的。
int Area(int length , int width ) ;
或 int Area ( int , int ) ;
69.函數定義
函數定義與函數聲明相對應,指函數的具體實現,即包括函數體。如:
int Area( int length , int width )
{
// other program statement
}
70.函數調用
指定被調用函數的名字和調用函數所需的信息(參數)。
71.函數名
與函數體相對,函數調用時引用之
72.函數類型
(1) 獲取函數并返回值。
(2) 獲取函數但不返回值。
(3) 沒有獲取參數但返回值。
(4) 沒有獲取參數也不返回值。
73.形式參數
函數中需要使用變元時,將在函數定義時說明需要接受的變元,這些變元稱為形式參數。形式參數對應于函數定義時的參數說明。其使用與局部變量類似。
74.實際參數
當需要調用函數時,對應該函數需要的變元所給出的數據稱為實際參數。
75.值傳遞
函數調用時形參僅得到實參的值,調用結果不會改變實參的值。
76.引用傳遞
函數調用時形參為實參的引用,調用結果會改變實參的值。
77.遞歸
函數的自我調用稱為遞歸。每次調用是應該有不同的參數,這樣遞歸才能終止。
78.函數體
與函數名相對,指函數最外邊由{}括起來的部分。
79.作用域
指標識符在程序中有效的范圍,與聲明位置有關,作用域開始于標識符的生命處。分:局部作用域,函數作用域,函數原型作用域,文件作用域,類作用域。
80.局部作用域
當標識符的聲明出現在由一對花括號所括起來的一段程序內時,該標示符的作用域從聲明點開始到塊結束處為止,此作用域的范圍具有局部性。
81.全局作用域
標識符的聲明出現在函數,類之外,具有全局性。
82.類作用域
指類定義和相應的成員函數定義范圍。
83.全局變量
定義在任何函數之外,可以被任一模塊使用,在整個程序執行期間保持有效。當幾個函數要共享同一數據時全局變量將十分有效,但是使用全局變量是有一定弊端的:全局變量將在整個程序執行期間占有執行空間,即使它只在少數時間被用到;大量使用全局變量將導致程序混亂,特別是在程序較復雜時可能引起錯誤。
84.局部變量
定義在函數內部的變量。局部變量只在定義它的模塊內部起作用,當該段代碼結束,這個變量就不存在了。也就是說一個局部變量的生命期就是它所在的代碼塊的執行期,而當這段代碼再次被執行時該局部變量將重新被初始化而不會保持上一次的值。需要注意的是,如果主程序和它的一個函數有重名的變量,當函數被調用時這個變量名只代表當前函數中的變量,而不會影響主程序中的同名變量。
85.自動變量
由auto修飾,動態分配存儲空間,存儲在動態存儲區中,對他們分配和釋放存儲空間的工作是由編譯系統自動處理的。
86.寄存器變量
存儲在運算器中的寄存器里的變量,可提高執行效率。
87.靜態變量
由連接器分配在靜態內存中的變量。
88.類
一種用戶自定義類型,有成員數據,成員函數,成員常量,成員類型組成。類是描敘C++概念的三個基本機制之一。
89.外部變量
由extern修飾的變量
90.堆
即自由存儲區,new 和delete 都是在這里分配和釋放內存塊。
91.棧
有兩個含義:(1)指內存中為函數維護局部變量的區域。(2)指先進后處的序列。
92.抽象類
至少包含一個純虛函數的類。抽象類不能創建對象,但可以創建指向抽象類的指針,多態機制將根據基類指針選擇相應的虛函數。
93.嵌套類
在一個類里可以定義另一個類,被嵌入類只在定義它的類的作用域里有效。
94.局部類
在函數中定義的類。注意在函數外這個局部類是不可知的。由于局部類的說明有很多限制,所以并不常見。
95.基類
被繼承的類稱為基類,又稱父類、超類或范化類。它是一些共有特性的集合,可以有其它類繼承它,這些類只增加它們獨有的特性。
96.派生類
繼承的類稱為派生類。派生類可以用來作為另一個派生類的基類,實現多重繼承。一個派生類也可以有兩個或兩個以上的基類。定義時在類名后加":被繼承類名"即可。
97.父類
即基類。見95基類的解釋。
98.子類
即派生類。見96派生類的解釋。
99.對象
有兩重含義:
1. 內存中含有某種數據類型值的鄰近的區域。
2. 某種數據類型的命名的或未命名的變量。一個擁有構造函數的類型對象在構造函數完成構造之前不能認為是一個對象,在析構函數完成析構以后也不再認為它是一個對象。
100. 數據成員
指類中存儲數據的變量。
101.實例化
即建立類的一個對象。
102.構造函數
是一個類的實例的初始化函數,將在生成類的實例時被自動調用,用于完成預先的初始化工作。一個類可以有幾個構造函數,以不同的參數來區別,即構造函數可以被重載,以便不同的情況下產生不同的初始化;也可以沒有構造函數,此時系統將調用缺省的空構造函數。需要注意的是構造函數沒有返回類型。
103.成員初始化表
成員初始化表可用于初始化類中的任何數據成員,放在構造函數頭與構造函數體之間,用":"與構造函數頭分開,被初始化的數據成員的值出現在一對括弧之間,它們之間用逗號分開。
104.析構函數
是一個類的實例的回收函數,將在該實例結束使用前被自動調用,用于完成資源的釋放。一個類只可以有一個析構函數,當析構函數執行后,該實例將不復存在。析構函數同樣沒有返回值。
105.虛析構函數
由virtual 修飾的析構函數,當用基類指針釋放派生類對象時可根據它所指向的派生類對象釋放準確的對象。
106.繼承
面向對象的程序設計語言的特點之一。即一個對象獲得另一個對象的特性的過程。如將公共屬性和服務放到基類中,而它的各派生類除了有各自的特有屬性和服務外還可以共享基類的公共屬性和服務。這樣的好處是容易建立體系,增強代碼重復性。
107.單繼承
一個派生類只有一個基類,成為單繼承。
108.重繼承
一個派生類擁有多個基類,成為多繼承。
109.虛函數
在基類中說明為virtual并在派生類中重定義的函數。重定義將忽略基類中的函數定義,指明了函數執行的實際操作。當一個基類指針指向包含虛函數的派生對象時,C++將根據指針指向的對象類型來決定調用哪一個函數,實現了運行時的多態性。這里的重定義類似于函數重載,不同的是重定義的虛函數的原型必須和基類中指定的函數原型完全匹配。構造函數不能是虛函數,而析構函數則可以是。
110.純虛函數
在基類中只有聲明沒有實現的虛函數。形式為:
virtual type funname(paralist)=0。這時基函數只提供派生類使用的接口,任何類要使用必須給出自己的定義。
111.多態性
給不同類型的實體提供單一接口。虛函數通過基類接口實現動態多態性,重載函數和模板提供了靜態多態性。
112.復制構造函數
以自身類對象為參數的構造函數,如Z::Z(const Z&). 用在同類對象間進行初始化。
113.運算符重載
C++中可以重載雙目(如+,×等)和單目(如++)操作符,這樣可以使用戶像使用基本數據類型那樣對自定義類型(類)的變量進行操作,增強了程序的可讀性。當一個運算符被重載后,它將具有和某個類相關的含義,同時仍將保持原有含義。
114.靜態成員函數
成員函數通過前面加static說明為靜態的,但是靜態成員函數只能存取類的其他靜態成員,而且沒有this指針。靜態成員函數可以用來在創建對象前預初始化專有的靜態數據。
115.靜態成員變量
在成員變量之前加static關鍵字將使該變量稱為靜態成員變量,該類所有的對象將共享這個變量的同一拷貝。當對象創建時,所有靜態變量只能被初始化為0。使用靜態成員變量可以取代全局變量,因為全局變量是違背面向對象的程序設計的封裝性的。
116.私有成員
只能由自身類訪問的成員。
117.保護成員
只能由自身類及其派生類訪問的成員。
118.友元
被某類明確授權可訪問其成員的函數和類。
119.友元函數
在函數前加上關鍵字friend即說明了一個友元函數,友元函數可以存取類的所有私有和保護成員。友元在重載運算符時有時是很有用的。
120.友元類
被某類明確授權可訪問其成員的類
121.例外處理
報告局部無法處理某錯誤的基本方式。由try., throw , catch組成。
122.文件
是用于從磁盤文件到終端或打印機的任何東西。流通過完成打開操作與某文件建立聯系。
|