谷歌的C++風格指南says,「當前向聲明足夠時,不要使用#include」。轉發聲明或自足標題?
但C++ Coding Standards(薩特和Alexandrescu的),項目#23,「讓頭文件自給自足的行爲負責:。確保每個你寫的標題是編譯獨立的,有它包含的內容依賴於任何頭」
哪種策略最好?
谷歌的C++風格指南says,「當前向聲明足夠時,不要使用#include」。轉發聲明或自足標題?
但C++ Coding Standards(薩特和Alexandrescu的),項目#23,「讓頭文件自給自足的行爲負責:。確保每個你寫的標題是編譯獨立的,有它包含的內容依賴於任何頭」
哪種策略最好?
薩特和Alexandrescu的有關項目#22說:「不要過分依賴:不要#包括定義,當一個向前聲明將盡」。
就我個人而言,我同意這種說法。如果在我的類A中,我沒有使用類B的任何功能,也沒有實例化類B的對象,那麼我的代碼不需要知道如何創建類B。我只需要知道它存在。
向前聲明也打破循環依賴有用...
編輯:我也想了很多的意見,即標記B指出:有時會發生,你不包括文件a.hpp因爲它已經包含在你正在包含的文件b.hpp中,即使前向聲明足夠,它也選擇包含a.hpp。如果你停止使用b.hpp中定義的函數,你的代碼將不再編譯。如果b.hpp的程序員已經使用了前向聲明,那就不會發生了,因爲您將在代碼中的其他地方包含a.hpp ...
很好找!這解決了錯誤的二分法。 – traal
Sutter和Alexandrescu在這裏可能更正確。如果我轉發聲明class Foo;
由於某種原因存在於bar.h
並且不包括bar.h
,試着找到Foo
的聲明(特別是如果代碼庫很大)時,祝你好運。
我想說這兩個陳述是正確的。如果您的頭文件只包含一個指針或某個數據類型的引用,那麼您只需要一個前向聲明。
如果您的頭文件包含特定類型的對象,那麼您應該在其中定義該類型的頭文件中包含該頭文件。 Sutter和Alexandrescu的建議是告訴您不要依賴頭文件的使用者通過包含所需的類型定義來解決這些引用問題。
您可能還想看看Pimpl Idiom和Compilation firewalls(或C++11 version)。
謝謝,現在我看到我失蹤了。頭文件通常可以用一個不包含#的東西的前向聲明進行編譯。只有代碼文件仍然需要#include。 – traal
除了其他答案中所涵蓋的內容外,還有一些情況是您的必須由於相互依賴性而使用前向聲明。
FWIW,如果我只需要一個類型名稱,我通常會向前聲明它。如果它是一個函數聲明,我通常會包含它(儘管這些情況很少見,因爲非成員函數很少,並且必須包含成員函數)。
如果它是一個extern "C"
功能,我從來沒有向前聲明是因爲鏈接器不能告訴我,如果我搞砸了參數類型。
我相信他們都說完全一樣的東西。
假設你有一個方法通過引用你的頭文件需要一個Bar
,但方法在你的源文件中定義。標題中的前向聲明顯然足以讓標題獨立編譯。
現在我們來看看用戶代碼。如果客戶端只是傳遞從其他地方轉發的引用,那麼他們根本不需要定義Bar
,並且一切都很好。如果他們以某種其他方式使用Bar
,那麼該源文件是強制使用包含Bar
,而不是您的標頭的內容。事實上,如果你在頭文件中包含不需要的Bar
,那麼如果客戶端不再需要你的包含並將其刪除,突然之間他們的其他Bar
代碼就會退出工作,因爲它們從未將它正確地包含在它們自己的源文件中。
現在假設您的標題使用std::string
。在這種情況下,爲了頭文件獨立編譯,您必須包含<string>
,這兩個準則都會告訴您這麼做(谷歌是因爲前向聲明不行,而Sutter和Alexandrescu允許頭文件獨立編譯)。
這兩個建議是完全是彼此兼容。你可以同時關注他們兩個。它們絕不是相互排斥的 - 它不是一個「兩個或兩個」的情況。
的報頭可以使用前向聲明,當是所有需要,並且仍然是可編譯單機:
// Foo.hpp
class Bar; // Forward declaration
class Foo {
public:
void doSomethingFancy();
private:
Bar *bar;
};
在上述例子中,Foo.hpp
是自給自足的,即使Bar
類是使用前向聲明而不是包含Bar.hpp
標頭聲明的。
依賴管理是在C++中非常重要的:如果你改變一個頭文件,這取決於這個頭文件所有的翻譯單位需要進行編譯。這可能非常昂貴。因此,你希望你的頭文件是最小的,因爲它們不包含任何他們不需要包含的東西。這就是Google的建議。
如果您需要一定的分量,你應該包括組件的標題。但是,您不應該要求除組件的任何內容外的得到的聲明。也就是說,每個頭文件必須編譯而不包含任何其他內容。這是Herb和Andrei給出的建議。請注意,此只有適用於獲取聲明:如果要使用這些聲明中的任何一個,並且需要另一個組件,則可能還需要包含此組件的頭文件。
然而,這兩個建議一起去!他們都非常有價值,他們應該毫不妥協地跟蹤。這基本上意味着,如果您只需要聲明的類,那麼您更願意聲明一個包含其標題的類。也就是說,如果類僅顯示聲明,則在指針或引用定義,參數列表或返回類型中,聲明的類聲明爲就足夠了。如果您需要更多地瞭解課程,例如因爲它是定義的基類或類的成員,或者使用了一個對象在內聯函數中,您需要定義。實際上,如果您需要了解任何成員或班級規模,則需要定義班級。
一個有趣的扭曲是類模板:只有第一個類模板聲明可以定義默認參數!但是,類模板可以多次聲明。爲了在聲明類模板時使頭文件最小化,您可能希望爲涉及的類模板提供特殊的轉發頭,這些頭只會聲明類模板以及它的默認參數。然而,這是實施土地的方式...
我更喜歡Google。它降低了複雜性。 –
這兩種做法是完全可能的。 –
我不認爲他們互相矛盾。應用這兩個似乎是一個好主意 – Arvid