2012-09-22 127 views
3

我有一個關於虛擬函數調用優化的問題。我已閱讀的地方(而問題是,現在我無法找到文章),這可能是通過使用一個類似的結構來優化掉V-查表:虛擬函數調用的優化

// Base.h 
class Base 
{ 
public: 
    virtual void Foo() = 0; 
}; 

// Concrete.h 
class Concrete : public Base 
{ 
public: 
    virtual void Foo() 
    { 
     // do something; 
    } 
}; 

//Some.h 
extern Base* const g_object; 

// Some.cpp 
Concrete on_stack_concrete; 

Base* const g_object = &on_stack_concrete; 

訣竅應該使用一個const指針指向一個在堆棧上分配的變量(不是動態的),編譯器肯定會優化它。因此,無論用戶何時調用g_object-> Foo(),都可以執行部分​​操作,而無需v-table查找。

這是真的嗎?

預先感謝任何重播。

編輯:

這種構建體的可能的使用是限制具體實現的接口。當然,人們可以爭辯說,「受限制」的方法應該是私有的,但有時圖書館的其他模塊需要訪問對象的公共附加方法,而不允許用戶操縱這些方法。因此,例如使用#定義,我們可以創建類似的代碼:因此

// Some.cpp 
#ifdef _WIN32 
Win32Concrete concrete; 
#elif defined _UNIX 
UnixConcrete concrete; 
#endif 

Base* const g_global = &concrete; 

事實上這些類的聲明只能在CPP文件中定義,用戶不知道它們的存在。

問題不是爲什麼首先使用這樣的常量指針,但是如果可以在這種情況下優化掉v-table查找。

+2

這可能是編譯器相關的,你應該能夠驗證這看你的編譯器生成的程序集 – marcinj

+2

爲什麼使用指針,如果它總是指向同一個對象? –

+0

@BoPersson,我現在可以想到一個好的應用程序,例如跨平臺庫,僅向用戶公開抽象接口,而後端根據某些#defines針對各種平臺使用多個實現。這當然需要對具有不同定義集合的每個平臺進行多次編譯,但對於最終用戶來說,這沒有什麼不同。 –

回答

4

您好像被誤用了virtual

virtual實現運行時多態性。你描述的場景並不使用或不需要。在任何編譯環境中都不可能存在Win32ConcreteUnixConcrete

相反的:

// Some.cpp 
#ifdef _WIN32 
Win32Concrete concrete; 
#elif defined _UNIX 
UnixConcrete concrete; 
#endif 

Base* const g_global = &concrete; 

使用:

// CommonHeader.h 
#ifdef _WIN32 
typedef Win32Concrete Concrete; 
#elif defined _UNIX 
typedef UnixConcrete Concrete; 
#endif 

現在你的功能並不需要是虛的。

+0

這種方式初始化全局變量的一點是避免將它作爲參數傳遞給函數,因此這種情況就不會發生。它是全球性的,因此它在任何地方都可以訪問,所以你所提到的「概念」在我的理解中不會改變。 –

+0

@AdrianLis如果你直接訪問全局變量,那麼爲什麼要創建'Base * const g_object'呢?你可以使用'on_stack_concrete.foo()'代替。在這種情況下,您可以避免查找。 –

+0

因爲具體類可以向用戶展示他不應該直接使用的不同(額外)接口。這就是爲什麼指向基類的指針是爲了將界面僅限於重要的東西。我將編輯問題以顯示可能的用法。 –

1

解決此問題的最簡單方法是使需要訪問Concrete類的受限方法朋友的類。然後他們可以完全訪問課程,其他人只能獲得公共訪問權限。

如果這樣做不可行,可以將實現放在基類中,並且保護所有受限制的方法,然後派生出一個公開受保護方法的特殊類。

class Concrete 
{ 
public: 
    void foo() { ... } 
protected: 
    void bar() { ... } 
}; 

class ConcretePrivate : public Concrete 
{ 
public: 
    void bar() { Concrete:: bar(); } 
}; 

ConcretePrivate g_globalPrivate; 
Concrete& g_global = g_globalPrivate; 

使用g_global的代碼只能訪問具體的方法。使用g_globalPrivate的代碼可以訪問ConcretePrivate方法。

1

這是在Doom 3源代碼(https://github.com/id-Software/DOOM-3-BFG/)中使用的方法,例如neo/framework/FileSystem。^ h定義如下:

extern idFileSystem *  fileSystem; 

和新/框架/ FileSystem.cpp定義是:

idFileSystemLocal fileSystemLocal; 
idFileSystem *  fileSystem = &fileSystemLocal; 

我能找到關於它的唯一討論的是:http://fabiensanglard.net/doom3/

idTech4高水平對象都是具有虛擬方法的抽象類。這通常會涉及性能問題,因爲每個虛擬方法地址在運行時調用它之前都必須在vtable中查找。但是有一個「竅門」可以避免這種情況。

由於在數據段中靜態分配的對象具有已知類型,因此編譯器可以在調用commonLocal方法時優化掉vtable查找。