#pragma once 與#ifndef,#define,#endif的差別 、重複定義等相關問題
今天寫程式時,對編譯預處理和#pragma once,#ifndef,#define,#endif產生了一些困惑,以前都是知道個大概,今天一下子查了很多資料,下面就是一些我現在對這個問題的認識,弄清楚這些,再加上我轉的一篇.h和.cpp區別的部落格,對於認清重定義,預處理幫助很大
.cpp檔案是一個編譯單元,obj檔案是編譯的輸出檔案,連結是將所有地obj檔案連結起來組成一個exe。連結過程中如果有不同的cpp檔案中包含相同的函式名,變數名(注意不包括變數的引用、函式的宣告、以及static函式和變數),連結將會報重複定義的錯誤,很多時候如果把函式和變數的定義寫在.h檔案,多個cpp包含此.h檔案,就極可能會發生這樣的錯誤,所以應該把函式的實現、變數的定義寫在.cpp中,在.h中宣告。這裡有一種情況是例外,就是模板函式,其實STL容器的原始碼實現就都寫在.h中,那是因為模板有模板的機理,在這裡就不展開了。
上述是在發生在連結時期的重定義,是指兩個或多個cpp之間有衝突的變數和函式,是連結時發現的。還有一種重定義可能會發生在編譯時期。在同一個編譯單元,也就是cpp中,多數cpp可能存在巢狀包含,一個頭檔案被一個cpp包含了兩次,不僅造成編譯效率降低,而且如果此標頭檔案中含有定義,那就會被重定義。而#pragma once 與,#ifndef,#define,#endif就是這個問題的解決辦法。但兩者之間又有一些小區別:
1,、 #pragma once是和檔案繫結的,有檔案標誌,編譯時會檢查標頭檔案有沒有被編譯過,而不用進入檔案中檢查。編譯效率會比預處理頭方法高,但如果程式中有同一個.h檔案的多個副本,一個cpp不慎包含了多個副本.h檔案,那麼#pragma once對此是失效的,因為他只認檔案不認程式碼。
2、#ifndef,#define,#endif是和巨集繫結的,編譯時編譯器會進入標頭檔案中,檢查巨集有沒有被定義,雖然效率比不上#pragma once,但上面 #pragma once失效的情況,在這裡不會發生。此外使用#ifndef要防止多個.h檔案的巨集重名,也很煩。
3、#pragma once由編譯器提供保證,是平臺相關的,而#ifndef,#define,#endif是語言支援的,所以移植性好於#pragma once,但目前看來#pragma once似乎移植起來也基本沒什麼問題。
4、有方法把這兩種方法結合起來用,但看起來並沒有什麼卵用,反而增加了程式碼閱讀者的困擾