2009-01-17 40 views
54

我有一個有很多小函數的類。通過小函數,我的意思是函數不會做任何處理,只是返回一個字面值。例如:在C++的頭文件中編寫函數定義

string Foo::method() const{ 
    return "A"; 
} 

我創建了頭文件「Foo.h」和源文件「Foo.cpp」。但由於函數非常小,我正在考慮將其放入頭文件本身。我有以下問題:

  1. 是否有任何性能或者如果我把在頭文件中,這些函數的定義等問題?我會有很多這樣的功能。
  2. 我的理解是,編譯完成後,編譯器將展開頭文件並將其放在包含它的位置。那是對的嗎?

回答

61

如果函數小(的機會,你要改變它往往是低),如果該函數可以被放入標題,而不包括其他頭的無數(因爲你的函數依賴於他們),它完全有效。如果你聲明它們extern內聯,則編譯器需要給它相同的地址爲每個編譯單元:

headera.h

inline string method() { 
    return something; 
} 

成員函數被隱式內聯,只要它們的內部定義他們的班級。同樣的東西對他們來說也是如此:如果他們可以毫無麻煩地放入標題中,那麼確實可以這樣做。

因爲函數的代碼放在頭文件中並且是可見的,所以編譯器能夠內聯調用它們,也就是將函數的代碼直接放在調用位置(不是因爲你把內聯放在了前面但更多的是因爲編譯器決定這種方式,只是內聯而已,這是編譯器的一個暗示)。這會導致性能提高,因爲編譯器現在可以看到參數與函數本地的變量匹配,並且參數不會相互混淆 - 最後但並非最不重要的是,函數幀分配不再需要。

我的理解是編譯完成後,編譯器將展開頭文件並將其放在包含它的位置。那是對的嗎?

是的,這是正確的。該函數將在您包含其標題的每個地方定義。編譯器會關心只將其中一個實例放入生成的程序中,通過消除其他實例。

+0

謝謝。所有這些小功能都是虛擬的。這會對內聯有什麼影響嗎?我認爲在源文件中編寫函數體並標記爲內聯比直接在頭中編寫要好。我擔心,如果所有這些函數都在那裏定義,那麼頭文件的可讀性就會降低。 – 2009-01-17 15:20:38

+0

如果編譯器可以找出虛函數調用的方向,它也可以內聯:b * b_ = new d;度特(B_); //如果內聯doit,它會看到b_是d。那麼它可以像在d中一樣內聯虛函數定義的代碼。虛擬使得它更加困難,但並非不可能 – 2009-01-17 15:29:40

+0

但我同意你的看法:我常常不願意將代碼放入標題中,因爲當我更改它時,它會影響所有調用它的代碼,而且通常在標題中定義需要包括至少有一個代碼依賴的其他頭文件。 (對於簡單的吸氣者,我並不總是這樣,我把它們放在那裏)。 – 2009-01-17 15:31:33

4

由於頭文件中的實現隱式內聯,因此性能會有所提高。正如你所提到的你的功能很小,內聯操作對你來說是非常有益的。

關於編譯器的說法也是如此。編譯器—除了在頭文件或.cpp文件中的代碼之間內聯—之外沒有區別。

2
  1. 如果你的功能很簡單,把它們內聯,你就必須把它們粘在頭文件中。除此之外,任何公約都是這樣 - 公約。

  2. 是的,編譯器在遇到#include語句時展開頭文件。

1

這取決於適用於你的情況,但編碼標準:

小功能,而無需環路和別的應該內聯有更好的表現(但稍大一些的代碼 - 對於某些約束或嵌入式應用中很重要)。

如果頭部中有函數的主體,則默認使用inline(d)(這對於速度來說是件好事)。

在編譯器創建目標文件之前,調用預處理器(gcc的-E選項),並將結果發送到編譯器,該編譯器用代碼創建對象。

那麼短的答案是:

- 在頭部聲明功能是良好的速度(而不是空間) -

1

你應該用內聯函數。請閱讀Inline Functions以獲得更好的理解以及所涉及的權衡。

12

取決於你的編譯器,它的設置,它可以做任何下列的:

  • 它可能會忽略inline關鍵字(它 只是提示編譯器,而不是一個 命令),生成的立場 - 單獨的 功能。如果您的 函數超過編譯器相關的 複雜度閾值,它可能會這樣做。例如太多的 嵌套循環。
  • 它可能會決定比您的獨立功能 功能是 內聯擴展的一個好候選。

在很多情況下,編譯器可以更好地確定函數是否應該內聯,因此沒有必要再次猜測它。當一個類有很多小函數時,我喜歡使用隱式內聯,因爲在類中實現這個實現很方便。對於較大的功能來說,這並不適用。

要記住的另一件事是,如果你在一個DLL /共享庫中導出一個類(不是一個好主意恕我直言,但人們無論如何都這樣做),你需要非常小心使用內聯函數。如果內置的DLL編譯器決定的函數應該內聯,你有幾個潛在的問題:

  1. 編譯器建立使用DLL可能決定程序 不 內嵌的功能,因此它會 產生符號引用 函數不存在和 DLL將不會加載。
  2. 如果更新的DLL和改變 聯函數, 仍然會使用該功能的舊版本 ,因爲函數 客戶端程序得到了聯到客戶端代碼。
1

如果你這樣做,C++不會抱怨,但一般來說,你不應該這樣做。

當你#包含一個文件時,包含文件的全部內容被插入到包含點。這意味着您放入標題的任何定義都將複製到包含該標題的每個文件中。

對於小型項目來說,這可能不是什麼大問題。但是對於大型項目來說,這可能會讓編譯時間更長(因爲每次遇到相同的代碼都會重新編譯),並且可能會大大增加可執行文件的大小。如果您更改了代碼文件中的定義,則只需重新編譯該.cpp文件。如果您更改頭文件中的定義,則需要重新編譯包含頭文件的每個代碼文件。一個小小的變化可能會導致您必須重新編譯整個項目!

有時例外情況是對不太可能改變的平凡函數(例如函數定義爲一行)。