Reputation: 30615
Instead of using virtual functions where there is a lookup to the vtable pointer in the object, which then takes you to the vtable, containing a pointer to the function- would it not be possible to just contain a data member in the object which points directly to the function?
Upvotes: 2
Views: 730
Reputation: 73406
If I understand your question, you are looking for a way to implement polymorphism using a function pointer.
Well, it is possible but extremely cumbersome, error prone, but it'll be difficult to outperform de generated by your compiler for virtual function calls.
How to do it ?
The idea is to use a function pointer. In order to implement polymorphims, it must be in the base class.
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 !!
};
Code to test this construct:
B b;
D d;
B *pb = &b, *pd = &d;
pb->itest();
pd->itest();
Is it safe ?
There are severe limitations to this. For example:
Is it more performant than vtable lookup ?
No: look at the assembler executed for every polymorphic call to 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
Of course, the optimizer could inline the code, removing some push and pop but the general principle is that the code for the indirection will be generated.
Isn't vtable lookup performant enough ?
Well a vtable lookup is basically loading a function pointer from a fixed offset calculated by the compiler. The assembler code for callling a vitual test() function would look like this:
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
Conclusion
A vtable lookup is at least as performant that a call through a function pointer. The compiler takes care of all the initialisation and the most complex inheritance situations. Better use the powerfull of virtual functions instead of trying to manually outperform your compiler.
Upvotes: 1