2010-02-19 167 views
27

我正在閱讀一些C++代碼,並注意在頭文件和.cpp文件中都有「#include」。我猜如果我移動文件中的所有「#include」,讓我們說foo.cpp,它的頭文件foo.hh,並讓foo.cpp只包含foo.hh代碼應該工作,不考慮像缺點,效率等。應該在哪裏「包含」放在C++

我知道我的「突如其來」的想法在某種程度上必須是一個壞主意,但它的確切缺點是什麼?我是新來的C++,所以在我自己回答這個問題之前,我不想閱讀很多C++書籍。所以請把問題放在這裏尋求你的幫助。提前致謝。

回答

30

通常情況下,請將您的包含文件放在.cpp文件中,並且只有在.h文件不可用時。

在很多情況下,您可以使用forward declarations來刪除在其他頭文件中包含頭文件的需要:這可以幫助減少編譯時間,這可能會隨着項目的增長而成爲一個大問題。這是一個很好的習慣,因爲試圖在稍後的日期(當它已經是一個問題時)進行整理可能是一個完整的噩夢。

這個規則的例外是模板類(或函數):爲了使用它們,你需要看到完整的定義,這通常意味着把它們放在一個頭文件中。

+1

你對「extern模板」的解釋是非常一廂情願的想法。你會失望的。 –

+0

@nobugz:哦?真?我希望它不是...模板編譯速度是有關C++的最糟糕的事情之一。能夠將那些不需要看到它們的實現隱藏起來會是一個真正的好處。 – jkp

+0

聽起來好像你用'export'混淆了新的extern模板(這一切都被廢棄了)。 http://www.cppreference.com/wiki/keywords/export – luke

5

你會讓包括頭文件在內的所有其他文件在頭文件中過渡地包含所有的#include

在C++中(如在C中)#include由預處理器通過簡單地插入#include d文件中的所有文本來代替#include語句來處理。因此,如果有大量的#include,您可以將可編譯文件的大小誇大爲幾百千字節 - 編譯器需要爲每個文件解析所有這些。請注意,包含在不同位置的相同文件必須在每個地方重新進行重新清潔,這些地方是#include d!這可能會使編譯變慢。

如果您需要聲明(但未定義)標題中的內容,請使用forward declaration而不是#include s。

+0

或者,更正確地說,只要可以,就在你的.h文件中使用前向聲明而不是'#include'。有時候這是不可避免的。例如,如果A類有一個B類數據成員,那麼A.h將不得不包含B.h. –

1

如果#include .cpp文件,您可能會從鏈接器中加載大量「多重定義」錯誤。理論上講,你可以將所有內容都包含到單個翻譯單元中,但這也意味着每次更改單個文件時都必須重新構建所有內容。對於真實世界的項目,這是不可接受的,這就是爲什麼我們有像make這樣的連接器和工具。

+0

@Neil:如果標題的寫法不正確! – jkp

+0

@jkp。cpp文件不是標題 – 2010-02-19 15:57:13

+0

@ Neil:我的不好。是的,這不是很好的做法。 – jkp

1

在頭文件中包含頭文件很好,所以包含在C++文件中,但爲了最小化構建時間,通常最好避免在另一個頭文件中包含頭文件,除非絕對必要,尤其是如果許多C++文件包含相同的標題。

12

頭文件中的包含文件只應該是支持該頭文件所需的文件。例如,如果你的頭文件聲明瞭一個向量,你應該包含向量,但沒有理由包含字符串。你應該可以有一個空的程序,只包含那個單獨的頭文件並且將被編譯。

在源代碼中,當然,您需要包含您所調用的所有內容。如果你的頭文件都不需要iostream,但你需要它的實際來源,它應該單獨包含。

在我看來,包含文件污染是最糟糕的代碼腐敗形式之一。

編輯:嘿。看起來像解析器吃>和<符號。

1

.hh(或.h)文件應該用於聲明。

的.cpp(或.CC)文件都應該是用於定義和實現。

實現:第一,#include語句是文字#include "foo.h"從字面上複製foo.h的內容,並將其粘貼到include指令位於其他文件中的位置。

的想法是,其他一些文件bar.cpp和baz.cpp可能想利用存在於foo.cc.一些代碼通常情況下,bar.cpp和baz.cpp到#include "foo.h"來獲得他們想要使用的函數或類的聲明,然後在鏈接時,鏈接器將鉤住這些用法.cpp和baz.cpp到foo.cpp中的實現(這是鏈接器的全部要點)。

如果你把foo.h中的一切,並試圖做到這一點,你將有一個問題。假設foo.h聲明瞭一個名爲doFoo()的函數。如果該函數的定義(代碼)在foo.cc中,那很好。但是,如果將doFoo()的代碼移入foo.h,然後將foo.h包含在foo.cpp,bar.cpp和baz.cpp中,則現在有三個名爲doFoo()的函數的定義,並且您的鏈接器會發出抱怨,因爲你不能在同一個範圍內擁有多個同名的東西。

0

如果使用「包含防護」,則可以避免多重定義錯誤。

(begin myheader.h) 
#ifndef _myheader_h_ 
#define _myheader_h_ 
struct blah {}; 
extern int whatsit; 
#endif //_myheader_h_ 

現在,如果你在其他頭文件#包括「myheader.h」,它只會得到包含一次(由於_myheader_h_被定義)。我相信MSVC有一個「#pragma once」和相應的功能。

+0

'#pragma once'現在是事實上的標準 - gcc也支持它,我相信其他一些人也會這樣做。 –

1

在頭文件中使用#include沒有任何問題。這是一種非常普遍的做法,您不想讓用戶重負一個庫,同時還要記住需要其他不明確的標題。

一個標準的例子是#include <vector>。獲得你的矢量課程。還有一些內部CRT頭文件需要正確編譯vector類,這些東西你真的不需要也不想知道。

1

當一個頭文件應該只包含它所需要的,「需要什麼」比你想象的更流暢,而取決於用途時,你把標題。我的意思是說,一些頭文件實際上是庫或其他代碼的接口文件。在這些情況下,頭文件必須包含(並且可能包括#include)其他開發人員爲了正確使用庫而需要的所有內容。