2014-11-24 60 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