而不是使用虚拟函数查找对象中的vtable指针,然后将其带到包含指向函数的指针的vtable中 - 是不可能在对象中包含数据成员直接指向函数?可能为虚拟函数实现旁路虚拟表?
回答
如果我理解你的问题,你正在寻找一种使用函数指针来实现多态的方法。
嗯,这是可能的,但非常麻烦,容易出错,但它将难以胜过由编译器生成的虚拟函数调用。
怎么办?
这个想法是使用一个函数指针。为了实现多态,它必须在基类中。
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表查找是至少为高性能的,通过一个函数指针的调用。编译器负责所有的初始化和最复杂的继承情况。更好地使用虚拟功能的强大功能,而不是尝试手动胜过编译器。
没有开启优化,这个测试并没有真正展示任何有用的东西。 – 2014-11-24 23:17:36
@MarkRansom最好的做法可能是为OP的特定情况做一个基准测试,因为你不能从过于简化的演示代码上进行优化来推广。 – Christophe 2014-11-24 23:53:40
- 1. 实现虚拟路径
- 2. 虚拟智能卡实现
- 3. 虚拟函数机制实现
- 4. 重载函数(虚拟/非虚拟)
- 5. 虚拟IMG SRC现实路径
- 6. 虚基在虚拟函数表中的偏移为虚拟继承
- 7. 虚拟函数C++
- 8. 虚拟函数C#
- 9. 具有虚拟和非虚拟功能的unique_ptr :: get()函数
- 10. 实现虚拟键盘PC
- 11. fido-u2f虚拟实现
- 12. 问题,实现虚拟功能
- 13. 虚拟功能实现C++不工作
- 14. 没有虚拟构造函数但是虚拟析构函数
- 15. 虚拟函数C++:虚拟函数已经有一个主体
- 16. 覆盖虚拟功能到非虚拟功能可以吗?
- 17. 虚拟功能
- 18. 虚拟功能
- 19. 使用/不使用虚拟方法实现纯虚拟方法?
- 20. 虚拟化的实现也是虚拟的吗?
- 21. 如何调用(非虚拟)虚拟方法的原始实现?
- 22. 虚拟路径null?
- 23. 可能创建虚拟锚?
- 24. 虚拟函数可以被非虚函数覆盖吗?
- 25. WP7实现数据虚拟化
- 26. 为什么虚拟函数必须在超类中实现?
- 27. 为什么此方法在未标记为虚拟时表现为虚拟?
- 28. 虚拟表C++
- 29. 虚拟表
- 30. Rails 4虚拟记录(不是虚拟属性) - 可能吗?
如果编译器知道对象的静态类型,它可以将该调用优化为直接调用。我认为这种优化是常见的,但我不是100%确定的。如果编译器无法做到这一点,那么在对象中使用自己的指针(以及需要利用它的任何代码)可能会比仅在vtable中使用指针的编译器优化得少得多。 – 2014-11-24 20:33:29
如果在每个对象中添加所有指针,那么对象的大小是多少? – quantdev 2014-11-24 20:34:12
这意味着,您为每个虚拟成员在每个实例中都有一个数据成员。这有点快但体积更大。您只能为该呼叫节省一个间接寻址,但会浪费大量浪费的数据空间。 – Klaus 2014-11-24 20:34:40