2011-03-20 38 views
7

我在我的程序中有一個助手類,在我的程序的不同類中使用了許多靜態函數。例如。在多個翻譯單元中複製C++中的靜態成員函數嗎?

helper.h

Class helper { 
public: 
    static void fn1() 
    { /* defined in header itself */ } 

    /* fn2 defined in src file helper.cpp */ 
    static void fn2(); 
} 

助手僅具有靜態成員函數。所以,其他模塊不會創建幫助對象。輔助功能在其他模塊中使用,如:

A.cpp

#include "helper.h" 
A::foo() { 
    helper::fn1(); 
    helper::fn2(); 
} 

B.cpp

#include "helper.h" 
B::foo() { 
    helper::fn1(); 
    helper::fn2(); 
} 

該編譯器創建A.cppB.cpp的輔助功能單獨的副本?我閱讀了一些較早的帖子,並從編譯器將創建的答覆中收集了這些信息。但是當我從A.cppB.cpp打印fn1fn2的地址爲printf("Address of fn1 is %p\n", &helper::fn1);printf("Address of fn1 is %p\n", &helper::fn1);時,我得到相同的地址。我現在很困惑。有人可以澄清,如果我錯過了什麼。

我擔心輔助函數的多個副本(如果發生的話)的原因是我們試圖減少我們的可執行文件大小,並希望優化它。

回答

8

在類體內定義的函數被隱式標記爲inline。如果你使用該函數的地址,編譯器也會創建函數的常規副本(每個編譯單元),但鏈接器只會選擇其中一個副本以包含在可執行文件中,因此只有一個地址。

但是,內聯過程可以創建函數的多個副本,甚至超過編譯單元的數量。通常,通過消除參數傳遞和函數調用開銷以及通用子表達式消除的機會等,可以通過增加的優化來彌補複製代碼的增加的大小。雖然內聯經常被認爲是大小和速度之間的折衷,但大小增加往往可以忽略不計,甚至是負面的。

剛剛在類中聲明並在單個編譯單元中實現的函數在可執行文件中只有一個副本。

+0

+1。我應該仔細閱讀一下這個問題;)刪除我的帖子。 – Mahesh 2011-03-20 22:59:11

+0

@snkrish:是模板靜態成員函數還是模板靜態全局函數?如果它是一個靜態成員函數,那麼您可能會爲每個模板參數組合獲得一個新副本,但編譯單元的數量仍然不會影響它。如果使用靜態全局(或名稱空間)函數,則將在每個編譯單元中獲得新副本。 – 2011-03-21 01:53:22

+0

感謝您的詳細解答,現在我明白了地址相同的原因。我有一個後續問題,我不確定是否應該開始一個單獨的帖子。如果上述相同的fn2是使用模板編寫的:template static void fn2(); - 應該在頭文件本身中定義fn2,還是應該使用extern模板 helper :: fn2(){/ *定義在helper.cpp * /}中來優化可執行文件大小。編譯器是否會創建函數的一個副本,無論fn2是否用不同的T調用,而不管它是如何定義的? – cppcoder 2011-03-21 01:58:24

2

如果可見(例如,在類聲明中定義),那麼它被許多編譯器內聯地隱式聲明。

如果內聯,則是可以在某些情況下複製,在某些情況下內聯,在其他情況下內聯部分內聯。

它遵循一個定義規則(ODR),鏈接時會刪除在多個翻譯中找到的副本(除非您已啓用私有extern內聯,那麼最終可能會導致冗餘導出實現)。

如果你來自C:靜態在這種情況下不創建函數的唯一副本 - 它只是意味着你可以調用函數而不用聲明它的類的實例。

+0

謝謝我正在使用靜態調用fn1和fn2而不創建輔助對象。 – cppcoder 2011-03-21 02:40:54

+0

@srikrish我明白了。我只是提到它,因爲C程序員習慣於聲明爲靜態的函數來影響鏈接(當每個已知用途中沒有內聯時,它們會產生私人副本 - 與問題相關)。這是他們混淆的常見原因。因爲這一點,因爲我不知道你的背景......我添加了最後的細節。 – justin 2011-03-21 04:28:30

0

內聯靜態類方法與內聯自由函數沒有什麼不同。從理論上講,ODR規則意味着有一個函數實例,但實際上編譯器可能總是將其內聯,以便事實上沒有該函數的實例。

然而,非常行爲採取的地址功能將強制編譯器創建函數的一個實例的,它是編譯系統的強制執行ODR,從而確保您始終獲得問題相同的地址。