2015-12-04 30 views
1

我從事的是一個單元測試文化極低的項目。我們幾乎沒有單元測試,每個API都是靜態的。C++鏈接器對於僅從一個程序中的一個類使用的虛擬方法是否聰明?

爲了能夠單元測試我的一些代碼,我創建像

class ApiWrapper { 

    virtual int Call(foo, bar) { 
     return ApiCall(foo, bar); 
    } 
}; 

現在在我的功能,而不是包裝:

int myfunc() { 
    APiCall(foo, bar);   
} 

我做的:

int myfunc(ApiWrapper* wrapper) { 
    wrapper->Call(foo, bar);   
} 

這樣我能夠嘲笑這樣的功能。問題是有些同事抱怨生產代碼不應該受到可測試性需求的影響 - 我知道這是無稽之談,但卻是現實。

無論如何,我相信我在某處讀過某處,編譯器實際上很聰明,可以用直接調用替換未使用的多態行爲......或者如果沒有重寫虛擬方法的類,它將變爲「正常」。

我嘗試過了,在gcc 4.8上它沒有內聯或直接調用虛方法,而是創建了vt。

我試圖谷歌,但我沒有找到任何關於此。這是一件事,還是我誤解了......或者我必須做一些事情來解釋這個鏈接器,優化標誌或什麼?

請注意,雖然在生產這個類是最終的,但在測試環境中,它不是。這正是鏈接器必須明智和檢測到的。

+1

我叮叮噹噹,也許你正在尋找這個:http://stackoverflow.com/questions/733737/are-inline-virtual-functions-really-a-non-sense – NathanOliver

+2

我不清楚爲什麼你需要包裝。 –

+0

能夠嘲笑功能時,測試我的代碼依賴於它 – gsf

回答

1

如果C++編譯器確實知道實際類型是什麼,它將只使用直接調用來替換多態調用。

所以在下面的代碼片段,將優化:

void f() { 
    ApiWrapper x; 
    x.Call(); // Can be replaced 
} 

但在一般情況下,它不能:

void f(ApiWrapper* wrapper) { 
    wrapper->Call(); // Cannot be replaced 
} 

您還添加了兩個條件,你的問題:

如果沒有覆蓋虛擬方法的類,它將變爲「正常」。

這沒有幫助。 C++編譯器和鏈接器都不會查看類的總體來搜索是否存在任何繼承器。無論如何,這是徒勞的,因爲你總是可以動態加載一個新類的實例。

順便說一下,這種優化確實由一些JVM(稱爲虛擬化)執行 - 因爲在Java中有一個類加載器,它知道哪些類當前被加載。

在生產這個類是final

這將幫助!例如,鏗鏘會將虛擬呼叫轉換爲非虛擬呼叫,如果方法/方法的類別標記爲final

相關問題