2011-12-16 64 views
3

C和C++區分聲明和定義。爲什麼要將整個標題內容放在守衛標記內?

您可以多次聲明一個符號,但是您只能定義它一次。通過學習這一點,我有一個主意,把聲明外面的警衛,而定義侍衛內:

// declarations 

int foo(int x, int y); 
int bar(int x, int y); 

extern double something; 

class X; 

#ifndef _MY_HEADER_H_ 
#define _MY_HEADER_H_ 

#include "otherheader1.h" 
#include "otherheader2.h" 

// definitions of structures, classes. 

class X 
{ 
    // blah blah... 
}; 

#endif 

通過這種方式,我可以有我的頭什麼爲了我想要的。可能循環依賴不會是一個問題。

那麼,如果我們可以把聲明放在外面,爲什麼要用守護標記來保護整個標題呢?

我的理由是以下幾點:

你可能經常遇到的問題時,兩個頭指對方莫名其妙。您通常會得到一個未聲明的符號錯誤,您的第一個反應是包含必要的標題。但是當你的兩個頭文件碰巧包含對方時,你會發現神祕的錯誤。

A.H:

#ifndef A_H 
#define A_H 

#include "b.h" 

class A {B *b;}  

#endif 

b.h

#ifndef B_H 
#define B_H 

#include "a.h" 

class B {A *a;}  

#endif 

當b.cpp您包括您在B未申報A.H得到一個錯誤,但包含在報頭中的b.h。 (這是一個跆拳道的時刻。)

是因爲那首部警衛不會窩:

#ifndef B_H 
#define B_H 

#ifndef A_H 
#define A_H 

// B_H already defined no include here. 

class A {B *b;}  

#endif 

class B {A *a;}  

#endif 

如果你把聲明的防護罩外,則可以防止這種情況:

class B; // in b.h 

#ifndef B_H 
#define B_H 

class A; // in a.h 

#ifndef A_H 
#define A_H 

class B; // again from b.h 
// B_H already defined no include here, no redefinition. 

class A {B *b;}  

#endif 

class B {A *a;}  

#endif 

沒問題這裏。

更新:把標題納入警衛(抱歉是一個錯誤)。

+4

好吧,爲什麼不呢?爲什麼要投入努力來思考可能會發生什麼,以及什麼可能不在頭衛兵?你的方式在哪裏受益? – PlasmaHH 2011-12-16 11:58:39

+0

大部分教師和專業人員都不鼓勵在單個文件中實施課程。 – v01d 2011-12-16 12:00:19

+1

好吧,我會使用#pragma一次。 – harold 2011-12-16 12:01:55

回答

4

你錯過了一半的故事,當你只想到「聲明」。 C++還有一個「類定義」的概念,它是第三個新類型的動物 - 它是(成員函數的)定義(類)和聲明

由於類可能定義不止一次(就像任何定義),你不得包括與類定義頭文件不止一次。

現在想象一下,你有一些實用類Foofoo.hpp,你有兩個獨立的模塊a.hppb.hpp這都需要Foo。您的主程序必須包含a.hppb.hpp,但現在您試圖包含foo.hpp,因此Foo的類定義爲兩倍。

輸入包含警衛。

1

如果它是嚴格的頭部防護,則不需要 - 如果乘法包含,則聲明已經可見。

對此的另一個原因是,在嚴格的頭文件保護之外的聲明可能會禁用編譯器對包含多個頭文件的優化(也就是說,它會多次打開頭文件)。

2

因爲它可以讓您多次訪問標頭#include而不用擔心衝突。

儘管沒有必要嵌套一層,但如果您有多個嵌套(考慮包括h1,然後包括h2,因爲它需要包含h1),這是不可或缺的。

1

您的系統無法防止循環包含。例如:

頁眉答:

#include "B.h" 
#ifndef A_H_INCLUDED 
#define A_H_INCLUDED 
// ... 
#endif // A_H_INCLUDED 

頁眉B:

#include "A.h" 
#ifndef B_H_INCLUDED 
#define B_H_INCLUDED 
// ... 
#endif // B_H_INCLUDED 

源文件:

#include "A.h" // includes B, which includes A, which includes B, ... 
1

一個簡單的答案就是編譯速度。編譯器,比如GCC和其他可能的編譯器,可以檢測到一個完整的頭文件,並避免在多次訪問時讀取和重新處理這些文件。如果你沒有將整個文件包裝在標頭守衛中,那麼很有可能你會迫使你的編譯器在每次遇到時重新評估頭文件。