C++: prototype of a virtual pointer

585 views Asked by At

I am not sure if this is documented anywhere. We all know in case of virtual functions, each class holds a vptr which pointer to an array of function pointers called the virtual table. I want to know what is the prototype of the vptr. For ex, if a class is declared as follows,

class A
{
   int a;
   public: A(){}
   virtual void display();
   virtual void setValue(int x);
};

Now we are having two function pointer in the vtable of class A. How will a single vptr is capable of two definitions of different prototype?

Please let me know if my understanding is wrong.

Thx! Rahul.

3

There are 3 answers

0
Oliver Charlesworth On

The vptr is an implementation detail, and as such, it does not have a prototype.

0
Mankarse On

As Oli Charlesworth noted, virtual pointers are an implementation detail, so this question does not really make sense in terms of C++. That said, the following manual implementation of (some of the functionality) of virtual functions might be helpful for your understanding:

struct vtable {
    void (*display)(void*);
    void (*setValue)(void*, int);
};

void A_display(void *this_) { /*Cast this_ to A* and do A stuff*/ }
void A_setValue(void *this_, int x) { /*Cast this_ to A* and do A stuff*/ }

vtable A_vtable = {A_display, A_setValue};

struct A {
    vtable *vptr = &A_vtable;
    int a;
    public: A(){}
};

void B_display(void *this_) { /*Cast this_ to B* and do B stuff*/ }
void B_setValue(void *this_, int x) { /*Cast this_ to B* and do B stuff*/ }

vtable B_vtable = {B_display, B_setValue};

struct B {
    vtable *vptr = &B_vtable;
    int a;
    public: B(){}
};

void display(void *obj) {
    ((*static_cast<vtable**>(obj))->display)(obj);
}
void setValue(void *obj, int) {
    ((*static_cast<vtable**>(obj))->setValue)(obj, int);
}

Of course, this only gives a small subset of the capabilities of virtual functions, but it should be fairly straightforward to see that vptrs point to collections of pointers to functions, with fixed types.

0
Rob Kennedy On

The virtual table (in implementations that use such a thing) is a product of "compiler magic." It doesn't need to have a specific prototype because no C++ code ever uses it directly. Instead, the compiler custom-generates one to conform to each class that needs one. The compiler also generates the code to access each element, so it can guarantee that each one is accessed in a type-safe manner.

For example, the compiler knows that the slot for the A::setValue method holds a pointer to a function that matches the setValue signature because the compiler is the one that put it there in the first place. Furthermore, the only code that directly accesses that slot is machine code that the compiler generated, and prior to generating such code, the compiler already confirmed that the original C++ code was calling the setValue function. Thus, there is no worry that the setValue slot could ever hold anything other than a setValue-conformant function pointer. Nor is there any concern that some other slot might be accessed instead; if that happened, it would be a compiler bug, never something that would happen as a result of ordinary user code.

The elements of the table are never treated as a group, so there's no requirement that they all have the same type. At best, they all have a type of "general pointer or offset suitable for the CPU to jump to." Since it's not really C++ at that point, the "type" doesn't have to fit any particular C++ type.