C++關鍵字總結

const 關鍵字——常量

const 與define

define是預編譯器的編譯指令,它從C語言兼容下來,工作方式與文本編輯器中的全局搜索和替換相似。define定義的常量的意義在它開始的地方持續到文件結束,在預編譯階段,預編譯器已經將所有define刪除,並展開所有的宏定義。它單純只做文本替換,沒有類型安全檢查,define命令會很容易引入錯誤,並且這種錯誤很難發覺,因此C++中用const取代define預編譯指令。

    #define SIGMOID(x) (1/(1+exp(-x)))
    result  = SIGMOID(a+b);//沒有正確添加括號,會導致錯誤結果
    result  = SIGMOID((a+b));//正確

頭文件裏的const

const默認是內部鏈接,如果它被放在頭文件中,目的是爲了讓所有包含它的編譯單元能使用這個值,而且是僅讓包含頭文件的編譯單元可見。定義一個const時,必須初始化,除非用extern做出了外部引用。通常,C++編譯器不會爲const變量分配空間,但extern關鍵字會強制編譯器爲const變量分配存儲空間。因爲extern爲外部鏈接,爲了其他的編譯單元都能引用到const變量。變量必須要有存儲空間。
:由於編譯器不能避免爲const分配內存,所以const定義必須默認內部鏈接。在C++中,const常量是否被分配空間依賴於它如何被使用:對於基本數據類型的常量,編譯器會把它放到目標文件的符號表中而不分配存儲空間,而自定義的const對象則需要分配存儲空間(大對象)。還有一些情況下也需要分配存儲空間,例如強制聲明爲extern或取一個const的地址等操作。

const關鍵字與多線程安全

可重入是併發安全的保障,一個可重入的函數(函數沒有執行完成,由於外部因素或內部調用,又一次進入函數執行)在多線程的環境下可以放心使用。而爲了保證一個函數是可重入的,它必須使用任何(局部)靜態或非const全局變量。同時,不能返回任何(局部)靜態或非const全局變量的指針。

類中的const

在類中定義的非靜態const變量,這個類的不同的對象可以含有一個不同的值。const的初始化在類的構造函數的初始化列表中。

static const——編譯期間類裏的專屬常量

static意味着“不管類的對象被創建多少次,都只有一個實例”。必須在static const定義的地方對它初始化。C++用它來代替enum(枚舉型),來指示對象的共同屬性。它是爲整個類服務,而不是某個對象,所以它不能使用this指針(this指針是對成員函數調用時用來指示調用對象的),也不能在複製構造函數中被複制。如果你取某個類的專屬常量的地址或編譯器堅持要看到一個定義式,則用域名解析符定義一下

//GamePlayer.h中
class GamePlayer{
private:
    static const int NumTurns = 5;
    int scores[NumTurns];
    };
//GamePlayer.cpp中
const int GamePlayer::NumTurns;//NumTurns在class聲明中已經初始化,因此這裏不再設初值
const對象和成員函數

不修改數據成員的任何函數都應該聲明爲const,這樣它可以和const對象一起使用。
按位const:對象中的每個字節都不能變。
按邏輯const:可以以成員爲單位改變。

兩種實現按邏輯const的方法
一種是取this指針,並把它強制轉換成指向當前類型對象的指針,具體來說就是講將this強制轉換成普通指針。
另一種是使用關鍵字mutable,以指定一個特定的數據成員可以在一個const對象中被改變。


static關鍵字——靜態區、內部鏈接

static關鍵字有兩個作用,一個讓變量存在靜態區,另一個是讓錯誤限制在一個源文件內。讓局部變量聲明成static使局部變量存儲在靜態區,從而在程序的整個生命期都存在。同時,當static作用於全局變量時,該全局變量變爲內部鏈接,它的意思是“在文件的外部不可以使用這個名字”,從而使錯誤局部化。
static全局變量與全局變量的區別是靜態變量只初始化一次,防止在其他文件中被引用;static局部變量與普通局部變量的區別除了值在啓動程序時初始化一次外,就是它的值在程序的整個週期內都存在,兩次函數調用期間,它的值保持不變;局部靜態變量在函數調用之間的值保持不變,根據這個特性,可以用於記錄函數調用或類創建的一些信息。
static函數與普通函數的區別是static函數在內存中只有一份,而普通函數在每次被調用都維持一份拷貝。

extern關鍵字——外部變量

extern關鍵字用來聲明另一個文件中的全局變量。所以extern和static是矛盾的,不能同時使用。
extern “C”關鍵字
C++編譯器將extern “C”中的代碼單做C語言代碼處理。extern C大括號所包圍的範圍中,C++的名稱修飾機制不起作用,對於Visual C++,直接在變量名和函數名前加”_”。但對於Linux下的GCC,extern “C”後面的符號都是修飾後符號。
:在C語言中不支持使用extern關鍵字
這裏列舉我平時編程時遇到的問題及處理方案
在我用vs2012調用C語言寫的庫時,出現“error LNK2019: 無法解析的外部符號 clGetPlatformIDs“的錯誤。
原因通常是沒有包含相應的lib,也就是說鏈接器沒搜索到相應lib中的clGetPlatformIDs目標模塊,基於這種情況,我思考會不會是這個函數在頭文件中是C語言函數的聲明與定義,但是我又在C++代碼中包含該頭文件,導致其採用C++的名稱修飾機制而無法與C語言庫中符號鏈接。所以對於C++調用C庫,需要添加extern “C”關鍵字聲明。
通常的做法是定義宏

#ifdef __cplusplus
extern C" {
#endif

cl_int clGetPlatformIDs(...);

#ifdef _cplusplus
}
#endif

但是如果這個C源碼已經編譯成庫,但是模塊的頭文件中沒有包含extern “C”,則在C++文件中,需要添加

extern "C"{
    #include "cl.h"
    }

這相當於在cl.h頭文件中所有的聲明都添加了extern。

鏈接與const、static、extern——內存分配與讀寫
無鏈接性:在代碼塊中的局部變量(包括static局部變量)
外部鏈接:函數之外定義的所有變量(除了const變量)和函數默認爲外部鏈接性。在定義時使用extern關鍵字顯式指定標識符具有外部鏈接。也就是說在多個文件程序中,可以在文件並且只能在一個文件中定義全局變量,其他文件要使用該變量,要在變量前添加extern關鍵字。當有extern時,只是告知編譯器存在這個變量,編譯器並不爲該變量分配存儲空間,即真正的聲明;若沒有extern,則在聲明的同時,編譯器也爲該變量分配存儲空間。
內部鏈接:全局static變量和const變量爲內部鏈接,爲了使const具有外部鏈接以便讓另外一個文件可以對它引用,必須在當前文件裏明確把它定義爲extern並初始化。
應使用鏈接性爲外部的多文件程序的不同文件中共享數據,而使用鏈接性爲內部的靜態變量在同一文件中的多個函數間共享數據

volatile關鍵字

告訴編譯器“該變量不知道何時回改變“,防止編譯器依據變量的穩定性(短期內值不變)作任何優化。
volatile關鍵字與多度優化
在多線程環境下,即使合理地使用了鎖,也不一定能保證線程安全。

x=y=0
Thread1        Thread2
x=1            y=1
r1=y           r2=x

由於CPU的動態調度或編譯器的優化,執行程序時,有可能交換兩條毫不相干的相鄰指令的順序,導致r1=r2=0的情況的發生。而volatile關鍵字可以試圖阻止優化。
(1)阻止編譯器爲了提高速度將一個變量緩存到寄存器而不寫回。也就是說防止編譯器根據變量的穩定性作任何優化,假設要讀一個硬件中的寄存器,將使用這個關鍵字。無論何時需要volatile變量的值,編譯器都要硬着頭皮頭讀,即使該行之前剛剛讀過。
(2)阻止編譯器調整volatile變量的指令順序。(這一步其實只能阻止編譯器的優化換序,並不能 阻止CPU動態調度換序)
:阻止CPU亂序執行的唯一辦法是調用CPU提供barrier指令;

關鍵字restrict

關鍵字restrict只用於限定指針;該關鍵字用於告知編譯器,所有修改該指針所指向內容的操作全部都是基於(base on)該指針的,即不存在其它進行修改操作的途徑;這樣的後果是幫助編譯器進行更好的代碼優化,生成更有效率的彙編代碼。
最後注意一點,restrict是C99中定義的關鍵字,C++目前並未引入;在GCC可通過使用參數” -std=c99”
來開啓對C99的支持


enum枚舉類型

本質上,enum是一個int型,但C++不允許enum到int的隱式轉換。枚舉的類型名也是可選的。enum{ a,b,c}choice;可以立即定義一個enum實例。一個類中的枚舉在編譯期間分配值,不佔用對象的內存空間。但通常優先選擇static const而儘量不是用enum。枚舉常量的缺點是:它的隱含數據類型是整數,其最大值有限,且不能表示浮點數

register變量

關鍵字register只是告訴編譯器“儘可能快地訪問該變量”dawn並不能保證將變量放置在寄存器中。同時,register有許多限制,比如不能獲取register變量的地址,不能將其聲明爲全局或靜態變量,因爲register變量沒有內存地址。因此,最好避免使用。

以上,有些內容轉自網絡,如有侵權,請聯繫博主改正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章