2014-12-02 49 views
7

我使用#pragma once(或者你使用include guardsàla #ifndef...)基本上在我的C++項目的每個頭文件中。這是巧合,還是您在大多數開源項目中找到的例子(以避免依賴個人項目經驗的依賴)。如果是這樣,爲什麼不是另一種方式:如果我想多次包含頭文件,我使用相同的特殊預處理器命令,如果沒有,我將文件保持原樣。爲什麼在C++中不包括警衛?

+8

'#pragma once'不是標準的; '#ifndef/#define/#endif'是。 – 2014-12-02 23:12:00

+3

@ T.C.好。但問題基本上保持不變。 – NOhs 2014-12-02 23:15:00

+0

你可能想看看''(和'')。如果'#pragma once'或類似的東西是自動的,那麼兩個標題將會被削弱。 (雖然它仍然會反對讓正常的事情變得簡單和短暫,而不會讓不尋常的方式變得不可能,我的意思是,可以包括'#pragma redo'或其他類似的東西。) – Deduplicator 2014-12-02 23:16:57

回答

7

C++編譯器的行爲是根據它如何處理每個翻譯單元來指定的。翻譯單元是預處理器運行之後的單個文件。事實上,我們有一個在某些文件中收集聲明並將其稱爲「頭文件」文件的約定對編譯器或C++標準沒有任何意義。

簡而言之,標準沒有提供「頭文件」,所以它不能提供自動包含頭文件的保護。該標準僅提供預處理器指令#include,其餘僅僅是約定。沒有什麼能夠阻止你從前向聲明所有內容,而不是使用頭文件(除了憐憫誰應該維護那些代碼......)。

所以,頭文件並不是特殊的,有沒有辦法說「這是一個頭文件,保護它」,但爲什麼我們不能防範是獲得#include「d一切嗎?因爲#include比其他語言的模塊系統強大得多。 #include會導致預處理器粘貼到其他文件中,而不一定是頭文件。如果在不同文件中的一堆不同的命名空間中使用相同的using和typedef聲明,有時候這會很方便。您可以將它們收集到一個文件中,並在幾個地方將它們收集起來。你不希望自動包含防護措施阻止你這樣做。

使用#ifndef#define來有條件地包括標題也僅僅是約定。該標準沒有「包含警惕」的概念。 (現代編譯但實際上都意識到包括警衛。認識到包括警衛可以允許更快的編譯,但它無關,與正確地實現標準。)

獲取迂腐

標準不自由的使用這個詞「頭文件」,特別是關於C和C++標準庫。但是,#include的行爲在§ 16.2 *Source file* inclusion(emph。mine)下定義,並且它不會爲頭文件授予任何特殊權限。

我們正在努力將合適的模塊系統納入C++標準。

+1

有「標題」,有「源文件」。 「標題」不需要是實際的文件。 – 2014-12-02 23:50:48

+0

真的嗎?一定要告訴。那是因爲標準允許任何實現定義的方式提供<>中的東西嗎? – Praxeolitic 2014-12-02 23:51:38

+4

正確(或未指定,而不是實現定義)。所有這一切都需要編寫#include 會導致適當的聲明和定義被看到。編譯器可以進行特殊處理來識別標準頭名稱並將其內容硬編碼到它中,或者它可以從對象數據庫中提取一大塊AST數據。或者它可以讓預處理器在文本上包含文件(這是所有常見實現在實踐中所做的)。 – 2014-12-03 00:05:27

4

因爲歇斯底里的葡萄乾。

C++預處理程序與C預處理程序幾乎相同,C預處理程序是在40多年前設計的。當時的編譯器要簡單得多。預處理器甚至更簡單,只是一個愚蠢的宏處理器,甚至不是編譯器。雖然C++標準沒有指定它如何用於標準頭文件,但概念上#include仍然是40年前的版本:它使預處理器將指定文件的內容插入到包含文件中。

在一個簡單的20世紀70年代的C代碼庫中,沒有大量的相互依賴關係和子模塊,可能不需要包含防護。早期的預標準C沒有使用函數原型,這是目前大多數包含文件的使用。如果包含頭兩次導致錯誤,您可能可以重新安排代碼以防止其包含兩次。

隨着代碼庫的發展和變得越來越複雜,我認爲偶然包含頭兩次(可能間接通過其他頭文件)的問題變得更爲常見。一種解決方案是改變預處理器,使其變得更加智能化,但這需要每個人都使用新的預處理器,並且會讓它變得更大更慢。因此,開發了一種可以利用預處理器(#define#ifndef)的現有功能解決問題的約定,而不是通過防止兩次包含頭部,而是通過簡單地將頭部包括兩次頭部而無害化,因爲它在第一次後沒有效果它包括在內的時間。

隨着時間的推移,這個約定變得更加廣泛,現在幾乎是通用的,除了罕見的包含兩次頭文件的例子,並被設計爲以這種方式正確工作(例如<assert.h>)。

再以後,#pragma once被一些編譯器作爲替代推出,不可移植的辦法有同樣的效果,包括警衛,但當時有成千上萬的各種C語言預處理器的副本中使用這個詞四周,包括警衛已成爲常態。

因此,目前的行爲幾乎肯定是由於歷史原因。今天編寫的現代語言,對於當今功能強大的計算機,如果從頭開始設計,就不會使用類似C預處理器的東西。但C並不是在21世紀設計的。我認爲隨着時間的推移,包含守衛會議會慢慢建立起來,並且不需要對現有軟件進行任何更改就能使其工作。現在改變它會破壞依賴於當前行爲的未知數量的C和C++代碼,並且可能不是一種選擇,因爲向後兼容性對於C和C++都很重要。

0

我不得不同意,主要因素是歷史,偶爾你會看到依賴它們的代碼不在那裏。 MAME就是一個例子:它通過多次包含宏並且以不同的方式定義宏來從可讀的基於宏的文件構建複雜的數據結構(或至少上次我查看,前一段時間)。如果包括守衛是自動的,你會遇到需要一種方法來關閉它們的代碼。