2009-03-03 56 views
9

是否每個.C或.cpp文件都應該有一個頭文件(.h)?每個C或C++文件都應該有關聯的頭文件嗎?

假設有下面的C文件:

  1. MAIN.C

  2. Func1.C

  3. Func2.C

  4. Func3.C

其中main()位於Main.C文件中。如果有四個標題文件

  1. Main.h

  2. Func1.h

  3. Func2.h

  4. Func3.h

還是應該有所有.C文件只有一個頭文件?

什麼是更好的方法?

回答

3

通常每個.c/.cpp文件都會有一個.h文件。

4

通常最好爲每個.c文件創建一個頭文件,其中包含要顯示的.c文件中的函數聲明等。這樣,另一個.c文件就可以包含它所需函數的.h文件,如果它沒有包含的頭文件被修改,就不需要重新編譯。

2

這取決於。通常你有單獨的.c文件的原因將決定你是否需要單獨的.h文件。

28

因爲在編譯時通常沒有必要暴露給其他編譯單元,所以擁有main.h是很少見的。 main()本身需要公開鏈接器/啓動代碼,但它們不使用頭文件。

每個C文件可以有一個頭文件,或者很可能是相關的一組C文件的頭文件。

其中一個例子是,如果您有BTree實現,並且您在自己的C文件中添加,刪除,搜索等,以最小化代碼更改時的重新編譯。

在這種情況下,爲每個C文件分別創建頭文件是沒有意義的,因爲頭是API,即用戶庫的視圖。我懷疑用戶想要包含6個頭文件只是爲了能夠使用這些函數。將會有一個btree.h文件和一個單獨的btree.lib文件,其中包含從各個C文件構建的所有BTree對象。

另一個例子可以在標準的C頭文件中找到。我們不確定是否有多個C文件用於stdio.h函數(這就是我如何做的,但它不是唯一的方法),但即使存在,它們也被視爲一個單位API。您不必包含stdio_printf.hstdio_fgets.h等等 - 對於C運行時庫的標準I/O部分,只有一個stdio.h

+1

因爲你沒有提到模板,我沒有+1:( – Marcin 2009-03-04 22:46:46

+0

我更專注於C端,但爲什麼我會提到模板,甚至對於C++?我沒有看到它們對於使用什麼頭文件的適用性(它們可以進入頭文件,但是typedef也可以,並且我沒有明確地提到*它們) – paxdiablo 2009-03-04 22:57:16

1

沒有更好的方法,只有常見和不常見的情況。

更常見的情況是,當您有一個類/函數接口來聲明/定義。最好只有一個.cpp/.c的定義,以及一個聲明頭。 給他們一個相同的名字很容易理解他們是直接相關的。

但這不是一個「規則」,這是幾乎所有情況下的常見方式和最有效的方式。

現在在某些情況下(如模板類或一些微小的結構體定義),您不需要任何.c/.cpp文件,只需標題即可。例如,我們通常只在頭文件中有一些虛擬類接口定義,只有虛擬純函數或簡單函數。

而在其他極少數情況下(如一個假想的main.c/.cpp文件),如果不會總是要求允許從外部編譯單元的代碼來調用一個給定的編譯單元的功能。主要功能是一個示例(不需要頭文件/聲明),但還有其他一些功能,主要是代碼「將所有其他部分連接在一起」,並且不會被應用程序的其他部分調用。這是非常罕見的,但在這種情況下,標題沒有意義。

0

如前所述,通常每個源文件(.c或.cpp)都會有一個頭文件(.h)。

但是,你應該看看文件的凝聚力。如果各種源文件提供單獨的,可單獨重用的功能集合 - 理想的組織 - 那麼您每個文件肯定應該有一個頭文件。但是,如果這三個源文件提供了一組複合函數(這太大而不適合一個文件),那麼您將使用更復雜的組織。主程序使用的外部服務將會有一個頭文件 - 這將被其他需要相同服務的程序使用。還會有協作源文件使用的第二個頭文件,這些頭文件提供了這些文件共享的「內部」定義。

(也由大同說明):主程序通常不需要它自己的頭 - 沒有其它源代碼應該是使用所提供的服務;它使用其他文件提供的服務。

0

如果您希望從另一個編譯單元使用您的編譯代碼,您將需要頭文件。有些情況下您現在需要/想要擁有標題。

第一個這樣的情況是main.c/cpp文件。這個類不打算包含在內,因此不需要頭文件。

在一些情況下,可以有定義,其通過在運行時加載的DLL加載一組不同的實現的動作的頭文件。將會有不同的.c/.cpp文件集來實現相同頭文件的變體。這在插件系統中可能很常見。

10
  1. 頭文件不是強制性的。

  2. #include簡單的複製/粘貼任何文件包含(包括。在實際項目中常用的C源文件)

  3. 是全球性的頭文件一樣config.h幷包含常用的信息,如編譯時標誌和項目範圍常量constants.h

  4. 一個好的圖書館API設計應該是公開一個正式界面和一套頭文件,並使用一套內部的頭文件來實現所有的細節。這爲C庫增加了一個不錯的額外抽象層,而不會增加不必要的膨脹。

  5. 使用常識。 C/C++不是真的沒有它的人。

0

cpp/c文件通常用於執行,而h/hpp(hpp不常用)文件僅用於頭文件(僅用於原型和聲明)。 Cpp文件並不總是必須有一個與它關聯的頭文件,但它通常是這樣做的,因爲頭文件就像是cpp文件之間的橋樑,所以每個cpp文件都可以使用來自另一個cpp文件的代碼。

應強制執行的一件事是在頭文件中不使用代碼!由於重新定義,頭文件在任何大小的項目中都會中斷編譯的次數太多了。這只是當你在2個不同的cpp文件中包含頭文件時。頭文件應始終被設計爲包含多次。不應該包含Cpp文件。

0

一般來說,我不認爲.h和.c文件之間有明確的關係。在許多情況下(大多數情況下),代碼單元是一個具有公共接口(.h)和不透明實現(.c)的功能庫。有時候需要一些符號,比如枚舉或者宏,並且你會得到一個沒有相應的.c的.h,在某些情況下,你將得到一個沒有公共接口並且沒有對應的代碼塊.h

特別是有些時候,爲了便於閱讀,頭文件或實現(很少兩者)都非常大而且毛茸茸的,以致於爲了程序員的理智,它們最終被分解成許多較小的文件。

2

我喜歡把接口放入頭文件並在cpp文件中實現。我不喜歡編寫C++,我需要將成員變量和原型添加到頭文件中,然後再在C++中添加方法。我喜歡這樣的:

module.h中

struct IModuleInterface : public IUnknown 
{ 
    virtual void SomeMethod() = 0; 
} 

module.cpp

class ModuleImpl : public IModuleInterface, 
        public CObject // a common object to do the reference 
               // counting stuff for IUnknown (so we 
               // can stick this object in a smart 
               // pointer). 
{ 
    ModuleImpl() : m_MemberVariable (0) 
    { 
    } 

    int m_MemberVariable; 

    void SomeInternalMethod() 
    { 
     // some internal code that doesn't need to be in the interface 
    } 

    void SometMethod() 
    { 
     // implementation for the method in the interface 
    } 

    // whatever else we need 
}; 

我覺得這是實施分隔條件和接口的真正乾淨的方式。

+0

這很有效地將Java接口編程到C++ 。我非常喜歡!如果你沒有在繼承類中實現SomeMethod(),那麼會拋出一個錯誤? – deworde 2009-03-04 13:55:10

1

這是關於什麼代碼需要知道什麼其他代碼。您希望將其他文件知道的數量減少到最低限度,以便他們完成他們的工作。

他們需要知道一個函數存在,它們需要傳入哪些類型以及它將返回什麼類型,但不是它在內部執行的操作。請注意,從程序員的角度來看,瞭解這些類型的實際含義也很重要。 (例如,int是行,哪個是列),但代碼本身並不在意。這就是爲什麼明智地命名功能和參數是值得的。

正如其他人所說,如果cpp文件中沒有任何內容暴露給代碼的其他部分,如main.c通常的情況,那麼就不需要頭文件。

偶爾值得把所有你想暴露的東西放在一個單獨的頭文件(例如Func1and2and3.h)中,這樣任何關於Func1的知道的都知道Func2,但我個人並不熱衷於這一點,因爲這意味着你會隨着你真正想要的東西一起加載大量的垃圾。

摘要: 想象一下,您相信某人可以編寫代碼,並且他們的算法,設計等等都很好。你想使用他們寫的代碼。所有你需要知道的是給他們什麼發生,你應該給它什麼,以及你會得到什麼。這就是頭文件需要去的地方。

1

如果你的文件暴露了一個接口 - 也就是說,如果它具有將從其他文件調用的函數 - 那麼它應該有一個頭文件。否則,它不應該。

6

我以前一直遵循「依賴於」的趨勢,直到我意識到一致性,一致性和簡單性比保存創建文件的努力更重要,並且「即使它們很糟糕,標準也很好」。

我的意思是以下內容:.cpp/.h文件對幾乎是所有「模塊」最終結果。同時滿足這兩個要求可以節省大量的混淆和糟糕的工程設計。例如,當我在頭文件中看到某些東西的接口時,我確切知道在哪裏搜索/放置其實現。相反,如果我需要暴露之前隱藏在.cpp文件中的東西的界面(例如靜態函數變成全局的東西),我確切知道在哪裏放置它。

我看到不是遵循這個簡單的規則太多的壞後果。不必要的內聯函數,打破關於封裝,(接口和實現的(非)分離,錯位代碼等等的任何類型的規則 - 所有這些都是由於從未添加適當的兄弟頭文件或cpp文件。

所以:總是定義.h和.c文件。制定一個標準,遵循它,並安全地依靠就可以了。這種方式的生活更簡單,而簡單的軟件中最重要的東西。

2

了Bjarne Stroustrup的解釋在他的著作「C++程序設計語言」精美....

物理分區的單頭樣式是最有用的程序是小,它的部分不適合單獨使用。當使用名稱空間時,程序的邏輯結構仍然可以在單個頭文件中解釋。

對於較大的程序,在傳統的基於文件的開發環境中,單頭文件方法是行不通的。對公共標題的更改會強制重新編譯整個程序,並且幾個程序員對單個標題的更新很容易出錯。除非重點強調嚴重依賴命名空間和類的編程風格,否則隨着程序的增長,邏輯結構會惡化。

另一種物理組織讓每個邏輯模塊都有自己的標題來定義它提供的設施。每個.c文件都有相應的h。文件指定它提供的內容(它的接口)。每個。c模塊包含它自己的.h文件,通常還有其他的.h文件,它們指定了從其他模塊需要什麼以實現在其接口​​中通告的服務。這個物理組織對應於模塊的邏輯組織。多頭方法可以輕鬆確定依賴關係。單頭方法迫使我們查看任何模塊使用的每個聲明,並決定它是否相關。簡單的事實是,維護代碼總是在不完整的信息和本地視角下完成的。 更好的本地化導致更少的信息編譯模塊,從而更快的編譯..