1

I was trying to find more info on the matter but could only find this: In C++ why and how are virtual functions slower?

The answer says that the virtual call "Get[s] the right function address from the vtable into a register (the index where the correct function address is stored is decided at compile-time).".

But how does it know which is the correct function (address) in the first place? Doesn't it have to check which type the underlying object has in order to do so, thus being similar to a branching switch statement?

2
  • The implementation of virtual dispatch, at the object code level, is generally similar to equivalent switch-based branching. That's what that answer is saying. Both are slower than doing nothing Commented Oct 23, 2018 at 13:58
  • The crucial point is that every object that supports virtual dispatch holds a pointer to the vtable of its class. So a virtual method call foo->bar(args) would be compiled like a function pointer call foo->_Vtable[_Index_for_bar_method](foo, args), ignoring any casts. The vtable of each class can be statically initialized. So vtables make it possible to get away without explicit type checks that would require branching. Commented Oct 23, 2018 at 14:21

1 Answer 1

4

The C++ compiler creates the vtable for each class. If classes A and B both are sub-classes of class Base, and override methods in base, then the vtable for A and the vtable for B are constructed with pointers to the respective overridden methods.

For the code where the virtual method is called, the entry in the vtable for the object is loaded with the address of the correct method for the sub-class.

Assuming that class Base has four virtual methods, m1, m2, m3, and m4, the vtable will have 4 entries and m3 will be the third entry in the table. Sub-class A overrides methods m1 and m4, while sub-class B overrides them all. The vtable for A will have four entries, with pointers to A::m1, Base::m2, Base:m3, and A::m4, while B will also have four entries, with pointers to B::m1, B::m2, B::m3, and B::m4. At the call site where m3 is being invoked, the third vtable entry is loaded with the correct method pointer because the compiler constructed the tables this way.

(Of course, there is much more to vtable construction and layout than I have presented, so your mileage may vary.)

7
  • So if I understand correctly, at runtime it would only have to check which vtable to choose (A's or B's), yes? Commented Oct 23, 2018 at 14:40
  • It won't check which vtable to use. It will simply use the vtable of the object that is the receiver of the dispatch. The vtable of an object is always in the same well-known place within an object, as a hidden field. For obj->vmethod() it will do obj->_vtable[_vmethodConstantIndex]()`. See @amon's comment. Commented Oct 23, 2018 at 14:56
  • @ErikEidt but how does it know which vtable to use -say, if you have a vector<Base*> with the derived classes inside (A and B), and you wanna call m2() on all of them, how could the program know the object's type directly? Does it not have to go check where the pointer is to see which is the underlying type? Commented Oct 23, 2018 at 15:00
  • The vtable is a hidden field in the object. It fetches the vtable from this hidden field in the object. Invocation of a virtual method requires a receiver, so it uses that receiver and fetches the vtable from its hidden field. As it happens, we cannot invoke a virtual method without a receiver object. Commented Oct 23, 2018 at 15:01
  • @ErikEidt ah, I see now -so anytime you create a class that inherits from a parent owning a virtual method, a vtable is created inside that class? Commented Oct 23, 2018 at 15:05

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.