Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ : How does Virtual Function resolve "this" pointer scope issue?

(C++,MinGW 4.4.0,Windows OS)

All that is commented in the code, except labels <1> and <2>, is my guess. Please correct me in case you think I'm wrong somewhere:

class A {
public:
   virtual void disp(); //not necessary to define as placeholder in vtable entry will be
                        //overwritten when derived class's vtable entry is prepared after
                        //invoking Base ctor (unless we do new A instead of new B in main() below)
};

class B :public A {
public:
   B() : x(100) {}
   void disp() {std::printf("%d",x);}
   int x;
};

int main() {
   A* aptr=new B;             //memory model and vtable of B (say vtbl_B) is assigned to aptr
   aptr->disp();              //<1> no error
   std::printf("%d",aptr->x); //<2> error -> A knows nothing about x
}

<2> is an error and is obvious. Why <1> is not an error? What I think is happening for this invocation is: aptr->disp(); --> (*aptr->*(vtbl_B + offset to disp))(aptr) aptr in the parameter being the implicit this pointer to the member function. Inside disp() we would have std::printf("%d",x); --> std::printf("%d",aptr->x); SAME AS std::printf("%d",this->x); So why does <1> give no error while <2> does?

(I know vtables are implementation specific and stuff but I still think it's worth asking the question)

like image 729
ustulation Avatar asked Oct 19 '25 18:10

ustulation


1 Answers

this is not the same as aptr inside B::disp. The B::disp implementation takes this as B*, just like any other method of B. When you invoke virtual method via A* pointer, it is converted to B* first (which may even change its value so it is not necessarily equal to aptr during the call).

I.e. what really happens is something like

typedef void (A::*disp_fn_t)();
disp_fn_t methodPtr = aptr->vtable[index_of_disp]; // methodPtr == &B::disp

B* b = static_cast<B*>(aptr);
(b->*methodPtr)(); // same as b->disp()

For more complicated example, check this post. Here, if there are multiple A bases which may invoke the same B::disp, MSVC generates different entry points with each one shifting A* pointer by different offset. This is implementation-specific, of course; other compilers may choose to store the offset somewhere in vtable for example.

like image 61
hamstergene Avatar answered Oct 22 '25 08:10

hamstergene



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!