如果我理解您的问题,您正在寻找一种使用函数指针实现多态性的方法。
嗯,这是可能的,但非常麻烦,容易出错,但在虚函数调用方面很难胜过编译器生成的 de。
怎么做?
这个想法是使用函数指针。为了实现多态,它必须在基类中。
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 ?itest@B@@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,但一般原则是生成间接代码。
vtable 查找性能还不够吗?
vtable 查找基本上是从编译器计算的固定偏移量加载函数指针。调用 vitual test() 函数的汇编代码如下所示:
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
结论
vtable 查找至少与通过函数指针的调用一样高效。编译器负责所有初始化和最复杂的继承情况。更好地使用强大的虚函数,而不是试图手动超越你的编译器。