Why is vptr stored as the first entry in the memory of a class with virtual functions?
Asked Answered
A

2

7

For some compilers, if a class has virtual functions then its vptr can be accessed with the address of the first byte of its object. For instance,

class Base{
public:
    virtual void f(){cout<<"f()"<<endl;};
    virtual void g(){cout<<"g()"<<endl;};
    virtual void h(){cout<<"h()"<<endl;};
};

int main()
{   
   Base b;

   cout<<"Address of vtbl:"<<(int *)(&b)<<endl;

   return 0;
}

I know that it is dependent on different compiler behaviors. Since there is the case where vptr is stored as the very first entry, what is the advantage of doing this? Does that help improve performance or simply because it's easier to access vbtl using &b?

Agent answered 6/11, 2015 at 6:2 Comment(4)
Is the behaviour of "vptr accessed by first byte of object" vary on OS?Dorsoventral
It would be implementation dependent.Madox
"We know that if a class has virtual functions then its vptr can be accessed with the address of the first byte of its object". No, we don't.Newsome
Counter-example: early versions of GCC (prior to 3.2 I think) stored the vptr at the end of the class (unless it inherited a vptr from a base class). The reason was that this way, the start of the object had the same layout as a C struct with the same fields.Seitz
R
6

It's an implementation detail but indeed many implementations do this.

It's rather efficient and convenient. Suppose you need to call a virtual function for a given object. You have a pointer to that object and the virtual function index. You need to somehow find which function should be called with that index and for this object. Okay, you simply access the first sizeof(void*) bytes behind the pointer and find where the vtable resides, then access the necessary element of vtable to fetch the function address.

You could store a separate map of "vtable for each object" or something but if you decide that you want to store the vptr inside the object then it's only logical to use the first bytes, not the last bytes or any other place because with this approach you know where to find the vptr once you have a pointer to the object, no extra data required.

Rainarainah answered 6/11, 2015 at 7:33 Comment(0)
S
3

Although this is implementation defined there doesn't seem to be much of a real choice.

First of all we can see that you ether have to have a vptr or an embedded vtable. The later means that you will have to copy the vtable on construction and it consumes more memory, but will have the advantage of avoiding one pointer dereference on each method call. There's probably good arguments for both of them depending on the situation - most implementations have chosen to lower construction time and overall memory consumption instead of saving dispatch time.

When chosen vptr approach we see that we must keep binary compatibility of the layout of base and derived classes. First of all we can achieve this by (often) using one vptr, this vptr must for compatibility reasons live in the most basic class.

When dealing with simple inheritance the most straight forward way of converting between derived to base class is to keep the pointer value which will mean that the layout has to be first the fields for the base class followed by the addition derived classes contributes to it.

Now we're quite near the reason why to put the vptr first. It simply has to be near the beginning of the object as it has to live within the most basic part of the object.

Then for the reason we put it on offset 0 may be that it's a consistent offset that's available for all classes. You simply has no guarantee that there's any data that could be placed before the vptr.

Putting the vptr at offset 0 has some advantages too. If you know the object to have a vptr you know that you would have to look at offset 0 without needing to know the type of the object (more than it has a vptr). This can come handy for some debugging purposes (the vtable often contains enough information to deduce the actual type). Especially this makes the typeid and similar simpler to implement since you only have to look at the same offsets to retrieve the type_info node via predefined offsets - which means that you can share the actual code for typeid.

Spiny answered 6/11, 2015 at 7:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.