简体   繁体   中英

How does compiler resolves virtual functions accessing non-virtual data members C++

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"? 在此处输入图片说明

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

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM