簡體   English   中英

一個頭文件中的多個類與每個類一個頭文件

[英]Multiple classes in a header file vs. a single header file per class

無論出於何種原因,我們公司都有一個編碼指南,其中規定:

Each class shall have it's own header and implementation file.

因此,如果我們編寫一個名為MyString的類,我們將需要關聯的MyStringh.hMyString.cxx

還有其他人這樣做嗎? 有沒有人看到任何編譯性能的影響? 10000 個文件中的 5000 個類的編譯速度是否與 2500 個文件中的 5000 個類一樣快? 如果不是,差異是否明顯?

[我們編寫 C++ 代碼並使用 GCC 3.4.4 作為我們的日常編譯器]

這里的術語是翻譯單元,您真的希望(如果可能)每個翻譯單元有一個類,即每個 .cpp 文件一個類實現,並具有相應的同名 .h 文件。

以這種方式做事通常更有效(從編譯/鏈接的角度來看),特別是如果您正在做諸如增量鏈接之類的事情。 這個想法是,翻譯單元是孤立的,這樣,當一個翻譯單元發生變化時,你不必重建很多東西,如果你開始將許多抽象集中到一個翻譯單元中,你就必須這樣做。

此外,您會發現許多錯誤/診斷是通過文件名報告的(“Myclass.cpp 中的錯誤,第 22 行”),如果文件和類之間存在一對一的對應關系,它會有所幫助。 (或者我想您可以將其稱為 2 對 1 對應)。

被數千行代碼淹沒?

目錄中的每個類都有一組頭文件/源文件似乎有點過頭了。 如果課程數量接近 100 或 1000,甚至可能會令人恐懼。

但是根據“讓我們把所有東西放在一起”的理念與消息來源一起玩,結論是只有寫文件的人才有希望不迷失在里面。 即使使用 IDE,也很容易遺漏一些事情,因為當您使用 20,000 行代碼的源代碼時,您只會對任何與您的問題不完全相關的事情閉上思緒。

依賴變得循環?

我在使用模板代碼時遇到了這個問題,但我在常規 C++ 和 C 代碼中看到了類似的問題。

將您的源代碼分解為每個結構/類的 1 個標頭可以讓您:

  • 加速編譯,因為您可以使用符號前向聲明而不是包含整個對象
  • 類之間有循環依賴(§)(即類A有一個指向B的指針,B有一個指向A的指針)

在源代碼控制的代碼中,類依賴關系可能導致類在文件中上下移動,只是為了使頭文件能夠編譯。 在比較不同版本中的相同文件時,您不想研究此類移動的演變。

具有單獨的標頭使代碼更加模塊化,編譯速度更快,並且更容易通過不同版本差異研究其演變

(§) 請注意,如果您知道哪個類擁有哪個類,則可以在類之間建立循環依賴關系。 這是關於知道其他類存在的類的討論,而不是 shared_ptr 循環依賴反模式。

最后一句話:標題應該是自給自足的

但是,必須通過多個標題和多個來源的解決方案來尊重一件事。

當您包含一個標頭時,無論是哪個標頭,您的源代碼都必須干凈地編譯。

每個標題都應該是自給自足的。 您應該開發代碼,而不是通過對 10,000 多個源文件項目進行 grep 來尋找哪個標頭定義了您需要包含的 1,000 行標頭中的符號來僅僅因為一個枚舉而進行尋寶。

這意味着每個標頭要么定義或前向聲明它使用的所有符號,要么包括所有需要的標頭(並且僅包含所需的標頭)。

關於循環依賴的問題

下划線-d問:

你能解釋一下使用單獨的頭文件對循環依賴有什么影響嗎? 我認為不會。 即使兩個類都在同一個標​​頭中完全聲明,我們也可以輕松地創建循環依賴,只需在我們在另一個中聲明它的句柄之前提前聲明一個。 其他一切似乎都很好,但是單獨的標頭促進循環依賴的想法似乎很遙遠

underscore_d,11 月 13 日 23:20

假設您有 2 個類模板,A 和 B。

假設類 A (resp. B) 的定義有一個指向 B (resp. A) 的指針。 假設 A 類(對應 B)的方法實際上調用了 B(對應 A)的方法。

在類的定義和它們的方法的實現中都有循環依賴。

如果 A 和 B 是普通類,並且 A 和 B 的方法在 .CPP 文件中,則沒有問題:您將使用前向聲明,每個類定義都有一個標題,那么每個 CPP 將包含兩個 HPP。

但是因為你有模板,你實際上必須重現上面的模式,但只有標題。

這表示:

  1. 定義頭A.def.hppB.def.hpp
  2. 實現頭A.inl.hppB.inl.hpp
  3. 為方便起見,“天真”標題A.hppB.hpp

每個標頭將具有以下特征:

  1. A.def.hpp (resp. B.def.hpp ) 中,您有一個類 B (resp. A) 的前向聲明,這將使您能夠聲明對該類的指針/引用
  2. A.inl.hpp (resp. B.inl.hpp ) 將包括A.def.hppB.def.hpp ,這將使來自 A (resp. B) 的方法能夠使用類 B (resp. A) .
  3. A.hpp (resp. B.hpp ) 將直接包括A.def.hppA.inl.hpp (resp. B.def.hppB.inl.hpp )
  4. 當然,所有標頭都需要自給自足,並受標頭守衛保護

天真的用戶將包括A.hpp和/或B.hpp ,從而忽略整個混亂。

擁有這種組織意味着庫編寫者可以解決 A 和 B 之間的循環依賴關系,同時將兩個類保存在單獨的文件中,一旦您理解了該方案,就可以輕松導航。

請注意,這是一個邊緣案例(兩個模板相互認識)。 我希望大多數代碼不需要那個技巧。

我們在工作中這樣做,如果類和文件具有相同的名稱,則更容易找到東西。 至於性能,你真的不應該在一個項目中擁有 5000 個類。 如果這樣做,可能需要進行一些重構。

也就是說,有些情況下我們在一個文件中有多個類。 那時它只是文件主類的私有幫助器類。

+1 用於分離。 我剛剛進入一個項目,其中一些類位於具有不同名稱的文件中,或者與另一個類混為一談,並且不可能以快速有效的方式找到它們。 您可以在構建中投入更多資源 - 您無法彌補程序員損失的時間,因為(s)他找不到要編輯的正確文件。

除了簡單地“更清晰”之外,將類分離到單獨的文件中還可以讓多個開發人員更容易避免互相踩踏。 在提交對版本控制工具的更改時,合並將減少。

我工作過的大部分地方都遵循這種做法。 實際上,我已經為 BAE(Aust.)編寫了編碼標准,以及為什么不只是在沒有真正理由的情況下刻板刻畫的原因。

關於你關於源文件的問題,編譯的時間不是很多,而是首先能夠找到相關代碼片段的問題。 不是每個人都在使用 IDE。 並且知道您只查找 MyClass.h 和 MyClass.cpp 與在一堆文件上運行“grep MyClass *.(h|cpp)”然后過濾掉 #include MyClass.h 語句相比確實節省了時間......

請注意,對於大量源文件對編譯時間的影響,有一些變通方法。 有關有趣的討論,請參閱 John Lakos 的大型 C++ 軟件設計。

您可能還想閱讀 Steve McConnell 的 Code Complete 以獲取有關編碼指南的精彩章節。 實際上,這本書是一本很棒的書,我會定期回來閱讀。

注意 您需要可輕松在線獲得的第一版 Code Complete。 關於編碼和命名指南的有趣部分沒有出現在 Code Complete 2 中。

這樣做是常見的做法,尤其是能夠將 .h 包含在需要它的文件中。 當然性能會受到影響,但在它出現之前不要考慮這個問題:)。
最好從分離的文件開始,然后嘗試合並常用的 .h 文件以提高性能,如果你真的需要的話。 這一切都歸結為文件之間的依賴關系,這對每個項目都是非常具體的。

正如其他人所說,最佳實踐是從代碼維護和可理解性的角度將每個類放在自己的翻譯單元中。 然而,在大型系統上,這有時是不可取的 - 請參閱 Bruce Dawson 的 這篇文章中題為“使這些源文件更大”的部分,以討論權衡取舍。

同樣的規則在這里適用,但它指出了一些允許的例外情況,如下所示:

  • 繼承樹
  • 僅在非常有限的范圍內使用的類
  • 一些實用程序只是簡單地放在一個通用的“utils.h”中

我發現這些准則在涉及頭文件時特別有用:http: //google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Header_Files

兩個字:奧卡姆剃刀。 每個文件保留一個類,並將相應的標題保存在單獨的文件中。 如果您不這樣做,例如為每個文件保留一個功能,那么您必須創建關於什么構成功能的各種規則。 通過為每個文件保留一個類可以獲得更多收益。 而且,即使是像樣的工具也可以處理大量文件。 保持簡單,我的朋友。

每個文件只有一個類是非常有幫助的,但是如果您通過包含所有單個 C++ 文件的批量構建文件進行構建,那么它可以加快編譯速度,因為對於許多編譯器而言,啟動時間相對較長。

我很驚訝幾乎每個人都贊成每個班級有一個文件。 問題在於,在“重構”時代,可能很難使文件名和類名保持同步。 每次更改類名時,您也必須更改文件名,這意味着您還必須在包含文件的所有位置進行更改。

我個人將相關的類分組到一個文件中,然后給這樣的文件一個有意義的名稱,即使類名更改也不必更改。 擁有更少的文件還可以更輕松地滾動文件樹。 我在 Windows 上使用 Visual Studio,在 Linux 上使用 Eclipse CDT,兩者都有快捷鍵可以直接進入類聲明,因此查找類聲明既簡單又快捷。

話雖如此,我認為一旦一個項目完成,或者它的結構已經“固化”,並且名稱更改變得很少,每個文件都有一個類可能是有意義的。 我希望有一個工具可以提取類並將它們放在不同的 .h 和 .cpp 文件中。 但我不認為這是必不可少的。

選擇還取決於一個項目的類型。 在我看來,這個問題不值得一個非黑即白的答案,因為任何一種選擇都有優點和缺點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM