2

I was reading about the C++ Objects when I got this doubt. Suppose there are two classes

class X
{
     virtual int def() { };
}

class Y
{
     virtual int abc() { };
}
class Z : public X,public Y
{
     virutal int abc() { return a };
     int a;
}

Now what I understand is both Y and Z have the reference to Z::abc() in their virtual tables which helps to resolve the correct function to call. Consider

Y *y = new Z;
Z *z = new Z;
y->abc() // I understand this is done by reaching the vptr of y by this = this + sizeof (X)
and z->abc() // z has its own vptr reachable

My understanding is that in both the cases the "this" pointer is passed and after finding out the correct abc() to call, how does the programm reach the int value "a"? enter image description here

How does the compiler calculate the address of " int a" correctly based on type of the object passed?

user1429322
  • 1,266
  • 2
  • 24
  • 38
  • 3
    `X` doesn't know anything about `abc()`. – Jonathan Potter Oct 03 '13 at 02:17
  • That was a typo..My bad – user1429322 Oct 03 '13 at 02:19
  • @HansPassant I understand that "a" is at offset 8. But based on the type of calling object the this pointer will be at different places.My doubt is how does the function abc() gets to the starting memory location to get address of "a" as this + offset? Because this pointer will be pointing to different locations at different runtime. Is there any hidden pointer that always gives the starting point of the mem location? – user1429322 Oct 03 '13 at 02:30
  • This: "..after finding out the correct abc() to call.." is probably not how you should think about this. It already knows *where* the correct `abc()` function pointer can be found. Its the one in the `abc()` slot of the vtable. What is *in* that slot is dependent on the virtual inheritance hierarchy. The derivations provide the "what" that goes in there (a pointer to their implementation of `abc()`), but *callers* already know "where" to find it. – WhozCraig Oct 03 '13 at 02:31
  • http://stackoverflow.com/questions/3481548/why-do-we-need-this-pointer-adjustor-thunk – Hans Passant Oct 03 '13 at 02:38
  • There are different approaches, but the overall idea is that the ABI standarizes what address needs to be passed to the actual override, and the `this` pointer is adjusted before or during the virtual dispatch to ensure that. – David Rodríguez - dribeas Oct 03 '13 at 02:41

3 Answers3

3

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.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
1

you can treat

 virtual int abc() { return a };

as

 virtual int abc(Z * const this) { return this->a };

So for every non-static member function, there is a hidden pointer points to the object. So as long as the compiler can find the virtual method, it knows where a is.

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
CS Pei
  • 10,869
  • 1
  • 27
  • 46
0

To build on John's point, if a derived class overrides a base class method, the compiler will automatically create what's called a vpointer to a vtable in the derived class. The this pointer behind the scenes in a derived class points to the vpointer, which then points to the vtable for the derived class. This is how polymorphism works.

cowboydan
  • 1,062
  • 7
  • 15