2012-06-16 64 views
2

在F.B.爲期半年的C++課程中, Brokken(The C++ Annotations的作者)介紹了我們使用所謂的實現頭文件。我知道這是弗蘭克的慣例,但我從來沒有在其他地方看過它。因此,我會解釋一下這個概念,我很好奇別人對此的看法。是否有人熟悉「實現/內部頭文件」(* .ih)?

這個想法是,你只需將所有的#include指令放在一個文件中,你的類成員實現的實現(假設你沒有編寫類內定義),那麼.ih實現頭文件和而不是每個源文件中的#include這個文件。替代方案是
1)#include類頭中的所有內容或
2)#include所有頭文件都在每個源文件中分開。

到兩個備選方案的缺點是顯而易見的:
1A)你必須重新編譯所有S「添加任何需要一些額外的#include後,荷蘭國際集團這頭」來源#include
1b)你的頭文件,應該是你班上的一個清晰的界面,被一大堆指令所污染,用戶不知道它們用於什麼,他也不在乎。 2a)您必須在每個源文件中重複使用相同的標頭#include
2b)您的實施受所有這些#include的污染,使其看起來有點雜亂。

只是要清楚:

/* someclass.h(pp) */ 

#ifndef SOME_CLASS_H 
#define SOME_CLASS_H 

class SomeClass 
{ 
    //some private data members 
    public: 
     SomeClass(); 
     void sayHi() const; 
     // some more member functions 
    private: 
     // some private member functions 
}; 

#endif 

/* someclass.ih */ 

#include "someclass.h" 
#include <iostream> 
#include <vector> 
using namespace std; 

// namespace is now only used in my implementations, other people 
// including my headers won't accidentally import the entire std namespace. 

/* sayhi.cc */ 

#include "someclass.ih" 

void SomeClass::sayHi() const 
{ 
    cout << "sayHi() says hi!\n"; 
} 

現在,再次,問題是:有沒有人聽說過這樣的約定嗎?我說服任何人開始使用它嗎?我個人覺得這是一個非常有用的(甚至是顯而易見的)慣例,我有點驚訝,我沒有在其他地方見過它。

+2

胡說八道,因爲你必須爲每個類管理一個額外的文件,而且它不會改變一件事情,因爲'#include'只是簡單的複製粘貼。至少如果我沒有在你的問題中遺漏任何東西。 – Griwes

+0

不,它不會改變任何東西,但增加代碼可讀性。這將永遠是一個私人問題,但我更喜歡一個#包括「blabla.ih」,其中6個,特別是如果這6個重複在你的很多來源。 – JorenHeit

+1

廢話,每個實現文件將有不同的必需的標題 - 最有可能的。而且,如果將它們包含在另一層級的間接內容中,它會導致可讀性而不是提高它,因爲讀取文件的人必須查看另一個文件才能找出它包含的內容。 – Griwes

回答

1

我不明白。當你有類成員或類方法參數使用來自其他頭文件的類或struts或typedefs時,會發生什麼?並且,添加重新編譯的問題......這看起來更像是爲了獲得更多的工作。您應該只包含標題中頭需要的內容,並且只包含實現在impl中需要的內容。而已。

+0

在這種情況下,include(或前向聲明,如果足夠的話)必須在類頭中。我可以從經驗中得知,獲得/工作比例確實是有益的,因爲創建額外文件並不是那麼有用,而重新編譯可能需要一段時間(對我來說10分鐘是一段時間...) – JorenHeit

+0

@ user1428839,你仍然需要重新編譯所有可能被編譯的東西,因爲'#include'只是簡單的複製粘貼,正如我在OP下的註釋中標記的那樣 - 無論你如何包含所有內容,它總是以相同的格式編譯器。 – Griwes

+0

@Griwes這並非總是如此。如果您想要更改其中一個實現,需要通常位於.h文件中的不同#include僞指令,則必須重新編譯包含此.h文件的所有源文件。所有來源,我的意思是這個類的所有實現,但也包括其他類的所有實現。 – JorenHeit

3

我從來沒有見過這種用過的地方。大多數類都將其所有實現的功能放在一個文件中,因此它們在重複指定必要的標題時沒有問題。

+0

如果你選擇以這種方式組織代碼,那麼你是對的。當然,我是在一間教室裏長大的,那裏的「一個功能,一個文件」 - 規則(或者我應該說'法律')占主導地位。然而,對某些人來說這似乎毫無意義,它幫助我組織代碼。我從來不需要滾動瀏覽查找函數的源代碼,而只需點擊functionname.cc即可。花哨的編輯擺脫了這個問題,但我沒有使用它。儘管如此,這是一個品味問題,但我喜歡將源文件完全放在我的屏幕上。 – JorenHeit

+1

@ user1428839:這是一個「每個人都以完全不同的方式來完成」的問題。 – Puppy

+0

嗯,所以我是被遺棄的?難怪最近我感到如此孤獨......然後,每個人都喜歡流行音樂......這並不能讓它成爲好音樂! ;-) – JorenHeit

4

有趣的貼子。首先,我要評論user1428839的(簡稱海報下文)陳述有關.ih頭:

首先,海報寫道:

...的想法是,你只要把您的類成員的實現的 實現所需的所有#include指令......在一個文件中,.ih 實現標頭以及#在每個源文件中包含此文件。

按照規定,這是不正確的。爲避免混淆,句子的最後部分應該是:... .ih文件所屬類別的每個源文件。在下面 。ih接近類頭文件文件(比如someclass.h)只提供接口,應該只有聲明什麼可以聲明。所以,如果SomeClass有一個班級數據成員std::ofstream *d_out那麼沒有必要#include <fstream>。相反,它只需#include <iosfwd>即可。這會產生一個乾淨的類接口,如果包含不屬於源的源,但僅使用SomeClass,則會縮短其編譯時間。

接下來,海報狀態:

1A)你必須重新編譯所有來源#include'ing這個頭添加任何 需要一些額外的#包括的之後。

這不是真的。如果在接口中實際使用附加標頭聲明的功能,則僅需要。如果這不是真的,那麼就不需要完全重新編譯。完全重新編譯,如果添加新數據成員或者(但是那是糟糕的樣式,例如,您正在使用內聯成員)附加頭的元素在類接口中使用,則需要該對象。

通過海報提出

下一點:

1B)你的頭文件,那應該是你的類的界面清晰,被 有一羣偉大的#include指令,其中用戶有污染不知道他們用於什麼 ,他也不在乎。

正確,但重要的一點是,類頭變得太胖。每當外部源需要包含someclass.h時,編譯器還必須讀取所有這些附加頭文件,以及這些頭文件等包含的頭文件,而它只需要知道SomeClass的基本要素。這句格言是,用戶不必爲他/她不需要付費,在這種情況下編譯器必須讀取(通常很多)無用的(在SomeClass的上下文中)頭文件導致的額外編譯時間。

使用海報建議的.ih約定的替代方法之一是在需要的地方包含您需要的內容。事實上,我認爲這是一個很好的選擇,但它也使很多簿記和工作。通常,類成員實現需要相同的頭文件,並將它們放入一個文件中,實現頭還具有定義一個維護點的額外好處。是的,偶爾編譯器必須讀取某個特定的源代碼不需要的頭文件時,會有小的開銷,但這隻會發生一次:在類編譯期間(希望大概只是偶爾發生)。

恐怕有些反應,以海報的張貼是稍微誤解.ih方法的核心點的結果:

  • 類頭本身仍然是使用類可能
  • 來源一樣苗條不需要比嚴格要求的編譯時間更長
  • 類開發人員不必一次又一次地包含編譯類成員所需的一堆頭文件。相反,只需要包含一個適合課程要求的標題。

另一個回覆(DeadMG)的重點是把所有的源文件放到一個源文件中。這是一個風格問題。如果你爲一個特定的程序開發軟件,那麼你可能會把所有的源文件放到一個文件中,如果這符合你的個人喜好。我個人發現它很煩人,並且很難與這些文件一起工作,因爲我經常喜歡看到或並行處理許多功能的來源,但最終它當然是一種品味問題。然而,如果你開發的軟件有意在稍後的其他程序中重複使用軟件,例如,你開發了一個考慮將其添加到圖書館的類,那麼你絕對應該使用一個函數的類文件方案。考慮構造函數:類通常提供一系列構造函數,並選擇適合您的上下文的構造函數。如果該類的實現被放入一個源文件中,那麼你的最終程序會過於胖,因爲鏈接程序將不得不將實現你的構造函數的目標文件添加到你的程序中,以及該程序所需的目標文件,等等。所以你最終得到的是一個完全沒有意義的程序,它鏈接到許多許多額外的目標文件。單功能一文件原則可以防止這種情況發生。

+0

謝謝弗蘭克,我顯然不像你剛剛那樣徹底。 – JorenHeit