This is all implementation detail, and different compilers do different things. There are two common approaches to the problem but both depend on fixing what the this
pointer refers to in the function definition.
One approach is to have the this
pointer always referring to the complete object (at least at the level of the final overrider of that virtual function). In this approach, and in the presence of multiple inheritance, the entries in the vtable don't contain a pointer to the actual function, but a pointer to a trampoline that adjusts the this
pointer and then jumps to the overrider. The advantage of this approach is that for the leftmost non-empty base and in all cases of single inheritance the entry in the vtable is really the overrider and there is no cost associated. This is the approach taken in the Itanium C++ ABI, which is used in different OS, including Linux.
Another approach is to always pass the address of the subobject that first declared the member function. In this case the caller adjusts the this
pointer to refer to the subobject before jumping through the vtable. As in the first case, if there is no offset (first nonempty base, single inheritance) the compiler does not need to add any adjustment and no cost is incurred. Inside the implementation of the final overrider, the compiler uses offsets from the pointer of the base that declared the function, rather than offsets from the complete object. I believe this is the case in Windows/Visual Studio, but don't take my word here, as I don't have access to the VS C++ ABI to confirm this.
A third approach is to store in the vtable the adjustment directly. This approach is less common and incurs the cost of adjusting the offset even when it needs not be updated (by adding 0). I don't know of any current compiler that uses this approach.