2014-11-24 59 views
2

而不是使用虛擬函數查找對象中的vtable指針,然後將其帶到包含指向函數的指針的vtable中 - 是不可能在對象中包含數據成員直接指向函數?可能爲虛擬函數實現旁路虛擬表?

+1

如果編譯器知道對象的靜態類型,它可以將該調用優化爲直接調用。我認爲這種優化是常見的,但我不是100%確定的。如果編譯器無法做到這一點,那麼在對象中使用自己的指針(以及需要利用它的任何代碼)可能會比僅在vtable中使用指針的編譯器優化得少得多。 – 2014-11-24 20:33:29

+1

如果在每個對象中添加所有指針,那麼對象的大小是多少? – quantdev 2014-11-24 20:34:12

+2

這意味着,您爲每個虛擬成員在每個實例中都有一個數據成員。這有點快但體積更大。您只能爲該呼叫節省一個間接尋址,但會浪費大量浪費的數據空間。 – Klaus 2014-11-24 20:34:40

回答

1

如果我理解你的問題,你正在尋找一種使用函數指針來實現多態的方法。

嗯,這是可能的,但非常麻煩,容易出錯,但它將難以勝過由編譯器生成的虛擬函數調用。

怎麼辦?

這個想法是使用一個函數指針。爲了實現多態,它必須在基類中。

class B {     // Base class 
protected: 
    void (B::*fptest)(); // Function pointer to member function 
public: 
    void mtest()   // Class specific mebmer function 
    { cout << "Base test\n"; } 
    void itest()   // Polymorphic function 
    { (this->*fptest)(); }  // implemented by calling the poitner to member function 
    B() : fptest(&B::mtest) { } // Ctor must initialize the function pointer 
    virtual ~B() {}   
}; 

class D : public B {   // Derived class 
public: 
    void mtest()    // Class specific mebmer function 
    { cout << "Derived test\n"; } 
    D()      // Ctor 
    { fptest = reinterpret_cast<void(B::*)()>(&D::mtest); } // not sure it's this safe in case of multiple inheritance !! 
}; 

代碼來測試這個結構:

B b; 
D d; 
B *pb = &b, *pd = &d; 
pb->itest(); 
pd->itest(); 

安全嗎?

這有嚴重的侷限性。例如:

  • 您必須確保每個派生類都正確地初始化函數指針。
  • 在多重繼承的情況下,轉換爲基類成員的函數指針可能無法按預期工作。
  • 指針可能沒有超載。所以你需要爲每個可能的簽名指定一個不同的指針。這可能很奇怪。

它比vtable查找性能更高嗎?

編號:看,每多態調用執行itest()彙編:

; 41 : pd->itest(); // cod for the call for a derived object 
    mov ecx, DWORD PTR _pd$[ebp]  ; load the oject address 
    call [email protected]@@QAEXXZ   ; call B::itest 
; 16 : void itest() { (this->*fptest)(); } 
    push ebp 
    mov ebp, esp 
    sub esp, 68     ; 00000044H 
    push ebx 
    push esi 
    push edi 
    mov DWORD PTR _this$[ebp], ecx ; use address of object as parameter 
    mov eax, DWORD PTR _this$[ebp] ; load the function pointer 
    mov ecx, DWORD PTR _this$[ebp] ; " " 
    mov edx, DWORD PTR [eax+4]  ; call the function pointer 
    call edx 
    pop edi 
    pop esi 
    pop ebx 
    mov esp, ebp 
    pop ebp 
    ret 0 

當然,優化器可以內聯的代碼,刪除一些push和pop,但總的原則是,對於碼將產生間接性。

是不是足夠的查找性能?

一個虛擬表查找基本上是從編譯器計算的固定偏移量加載函數指針。對於callling一個vitual測試()函數是這樣的彙編代碼:

39 :  pd->test(); 
    mov eax, DWORD PTR _pd$[ebp] 
    mov edx, DWORD PTR [eax] 
    mov ecx, DWORD PTR _pd$[ebp] 
    mov eax, DWORD PTR [edx] 
    call eax 

結論

V表查找是至少爲高性能的,通過一個函數指針的調用。編譯器負責所有的初始化和最複雜的繼承情況。更好地使用虛擬功能的強大功能,而不是嘗試手動勝過編譯器。

+0

沒有開啓優化,這個測試並沒有真正展示任何有用的東西。 – 2014-11-24 23:17:36

+0

@MarkRansom最好的做法可能是爲OP的特定情況做一個基準測試,因爲你不能從過於簡化的演示代碼上進行優化來推廣。 – Christophe 2014-11-24 23:53:40