0

I know the following

Compiler constructs a virtual table for every class containing at least one virtual function. It also adds a pointer(v_ptr) to the base class(supposing it has virtual function(s)) and the pointer gets inherited by every derived class. When an object of a class is created that same pointer is made to point to the virtual table of class the object belongs to.

Now if I'm not wrong all above occurs at compile time only. If so then considering following example how is virtual keyword leading to dynamic binding.

#include <iostream>
using namespace std;

class base {
    public:
        virtual void show()
        {
            cout << "base\n";
        }
};
 class derived:public base{
    public:
        void show()
        {
            cout << "derived\n";
        }
 };


int main()
{
    base* ptr;
    derived drv1;
    ptr = &drv1;
    ptr->show();
    return(0);
}

In the above code how does dynamic binding occurs at the statement ptr->show(); since I think all is known at compile time to bind the function. show() is called via ptr which points to the base of derv1 which will be containing v_ptr pointing to the virtual table of its class and hence compiler knows which show() to call at the compile time only.

NOTE - The base of derv1 means the portion that is inherited from base class.

dRIFT sPEED
  • 103
  • 2
  • 1
    How virtual functions are implemented is not specified by the standard and different compilers can do different things. A vtable based implementation is *common* but it's not the only way virtual functions could be implemented. Also, a compiler may (and does indeed do so in some cases) devirtualize virtual function calls. – Jesper Juhl Aug 31 '23 at 04:57
  • "_Now if I'm not wrong all above occurs at compile time only._": No, the "_When an object of a class is created that same pointer is made to point to the virtual table of class the object belongs to._" part happens at runtime. It couldn't work any other way. The pointer must written into any object of the class type when it is created. – user17732522 Aug 31 '23 at 04:58
  • "_In the above code [...] since I think all is known at compile time to bind the function. [...] and hence compiler knows which show() to call at the compile time only._": That doesn't change anything. Of course the compiler is allowed to optimize the indirect function to a direct call if it can determine what function will actually be called at compile-time ("devirtualization"), but this doesn't affect any program semantics nor is a compiler required to do so. Also, the vptr and vtable are an implementation detail anyway. The language specification of virtual calls is independent of that. – user17732522 Aug 31 '23 at 05:01
  • It is called dynamic polymorphism for a reason. Dynamic in this case means runtime selection of the correct implementation (sometimes compilers can optimize to compile time, but this is no required). Static polymorphism for compile time also exists but that is usually done with templates. – Pepijn Kramer Aug 31 '23 at 05:01
  • "Now if I'm not wrong all above occurs at compile time only." You are *very* wrong. – n. m. could be an AI Aug 31 '23 at 05:17
  • @n.m.couldbeanAI I request you to please let me know what is the right thing then. – dRIFT sPEED Aug 31 '23 at 05:50
  • It all happens at run time. That be right. – n. m. could be an AI Aug 31 '23 at 06:04
  • The compiler is permitted to determine that specific call at compile time, since it can figure out the type of the object. The interesting case is when it can't know, such as in `void f(base* p) { p->show(); }`. – molbdnilo Aug 31 '23 at 07:29

1 Answers1

2

In this line

derived drv1;

an instance of a class is created. For this purpose, the constructor of the class is executed. (In this example, all constructors are implicitly declared and defined, but this fact makes no difference.)

  1. The constructor of derived begins. It immediately calls the constructor of base.
  2. The constructor of base fills in the instance's virtual table pointer to point to base's virtual dispatch table.
  3. The constructor of derived resumes after base's constructor ends.
  4. At this point it changes the instance's virtual table pointer to point to derived's virtual dispatch table.

The point is that the evolution of the instance happens at runtime, not at all at compile time.

Of course, optimizations may reduce things down to a level that nothing of this process is visible in the generated assembly ("devirtualization").

j6t
  • 9,150
  • 1
  • 15
  • 35