Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When exactly does the virtual table pointer (in C++) gets set for an object?

I know that for any class that has a virtual function or a class that is derived from a class that has a virtual function, the compiler does two things. First, it creates a virtual table for that class and secondly, it puts a virtual pointer (vptr) in the base portion for the object. During runtime, this vptr gets assigned and starts pointing to the correct vtable when the object gets instantiated.

My question is that where exactly in the instantiation process does this vptr gets set? Does this assignment of vptr happens inside the constructor of the object of before/after the constructor?

like image 908
Abhineet Mishra Avatar asked Oct 28 '11 20:10

Abhineet Mishra


2 Answers

This is strictly Implementation dependent.

For Most compilers,

The compiler initializes this->__vptr within each constructor's Member Initializer list.

The idea is to cause each object's v-pointer to point at its class's v-table, and the compiler generates the hidden code for this and adds it to the constructor code. Something like:

Base::Base(...arbitrary params...)
   : __vptr(&Base::__vtable[0])  ← supplied by the compiler, hidden from the programmer
 {

 }

This C++ FAQ explains a gist of what exactly happens.

like image 120
Alok Save Avatar answered Jan 03 '23 17:01

Alok Save


The pointer to the vtable is updated on entry to each constructor in the hierarchy and then again on entry of each destructor. The vptr will start pointing to the base class, and then will be updated as the different levels are initialized.

While you will read from many different people that this is implementation defined, as it is the whole choice of vtables, but the fact is that all compilers use vtables, and once you choose a vtable approach, the standard does mandate that the type of the runtime object is that of the constructor/destructor being executed, and that in turn means that whatever the dynamic dispatch mechanism is, it has to be adjusted as the construction/destruction chain is traversed.

Consider the following code snippet:

#include <iostream>

struct base;
void callback( base const & b );
struct base {
   base() { callback( *this ); }
   ~base() { callback( *this ); }
   virtual void f() const { std::cout << "base" << std::endl; }
};
struct derived : base {
   derived() { callback( *this ); }
   ~derived() { callback( *this ); }
   virtual void f() const { std::cout << "derived" << std::endl; }
};
void callback( base const & b ) {
   b.f();
}
int main() {
   derived d;
}

The standard mandates that the output of that program is base, derived, derived, base, but the call in callback is the same from all the four calls to the function. The only way that it can be implemented is by updating the vptr in the object as construction / destruction progresses.

like image 40
David Rodríguez - dribeas Avatar answered Jan 03 '23 17:01

David Rodríguez - dribeas