Dynamic Binding Through Virtual Functions in C++

Discussion in 'C++' started by poornaMoksha, Oct 1, 2011.

  1. poornaMoksha

    poornaMoksha New Member

    Joined:
    Jan 29, 2011
    Messages:
    150
    Likes Received:
    33
    Trophy Points:
    0
    Occupation:
    Software developer
    Location:
    India
    A virtual function is used where we want to allow the derived class to replace the implementation of the same function in base class. The compiler always calls the derived class function when called with object of the derived class or an object of base class holding the address of the derived class object. The derived class can fully replace the definition of the base class virtual function or could partially replace by calling the base class function in the implementation of the derived class function.

    A virtual function is declared by using the 'virtual' prefix to the declaration of that function. A call to virtual function is resolved at the run-time by analyzing the actual type of object that is calling the function. This is known as dynamic binding.if the object has one or more virtual functions, the compiler puts a hidden pointer in the object called a "virtual-pointer" or "v-pointer." This v-pointer points to a global table called the "virtual-table" or "v-table."

    Compiler creates a virtual table for a class that has at-least one virtual function. For example, if class Vehicle has virtual functions for engine() and body(), there would be exactly one v-table associated with class Vehicle, even if there were a thousand Vehicle objects, and the v-pointer of each of those Vehicle objects would point to the Vehicle v-table. The v-table itself has pointers to each of the virtual functions in the class. For example, the Vehicle v-table would have two pointers: a pointer to Vehicle::engine() and a pointer to Vehicle::body().

    Example



    Consider the following example without virtual functions :

    Code:
    #include<iostream>
    using namespace std;
    class Vehicle {
      public:
        void body ()
          { cout<<"\n could be metallic or non-metallic body\n"; }
      };
    
    class Car: public Vehicle {
      public:
        void body ()
          { cout<<"\n metallic body\n"; }
      };
    
    class bike: public Vehicle {
      public:
        void body ()
          { cout<<"\n non-metallic body\n"; }
      };
    
    int main () {
      Car c;
      bike b;
    
      Vehicle * v1 = &c;
      Vehicle * v2 = &b;
    
      v1->body();
      v2->body();
    
      return 0;
    }
    The output of the above code comes out to be :

    $ ./virtual

    could be metallic or non-metallic body

    could be metallic or non-metallic body


    Clearly the output indicates that it was not at all expected.

    Now lets try the concept of virtual functions :

    Code:
    #include<iostream>
    using namespace std;
    class Vehicle {
      public:
        [B]virtual [/B]void body ()
          { cout<<"\n could be metallic or non-metallic body\n"; }
      };
    
    class Car: public Vehicle {
      public:
        void body ()
          { cout<<"\n metallic body\n"; }
      };
    
    class bike: public Vehicle {
      public:
        void body ()
          { cout<<"\n non-metallic body\n"; }
      };
    
    int main () {
      Car c;
      bike b;
    
      Vehicle * v1 = &c;
      Vehicle * v2 = &b;
    
      v1->body();
      v2->body();
    
      return 0;
    }
    Note the virtual keyword used before the declaration of 'body()' in base class.
    Lets see the output :

    $ ./virtual

    metallic body

    non-metallic body

    Whola!!!!!, it worked as expected.

    Going a bit deeper



    Now, once we are clear with the usage aspect of virtual functions. Lets dig a bit deeper and see what happens inside :

    Suppose class Base has 5 virtual functions: virt0() to virt4().

    Code:
     class Base {
     public:
        // virtual function -1
        // virtual function -2
        // virtual function -3
        // virtual function -4
        // virtual function -5
        ...
     };
    Step -1: the compiler builds a virtual table containing 5 function-pointers. In context of base class, let us call it as Base::__vtable. It might look something like the following pseudo-code:

    Code:
    //Pseudo code
     FunctionPtr Base::__vtable[5] = {
       &Base::virt0, &Base::virt1, &Base::virt2, &Base::virt3, &Base::virt4
     };
    Step -2: The compiler adds a hidden pointer to each object of class Base. This is called the virtual-pointer. Think of this hidden pointer as a hidden data member:


    Code:
     class Base {
     public:
       ...
       FuncPtr* __vptr;  //provided by the compiler
       ...
     };
    Step -3: The compiler initializes this->__vptr within each constructor. The idea is to cause each object's virtual-pointer to point at its class's virtual-table, as if it adds the following instruction in each constructor's init-list:

    Code:
     Base::Base(/* Some params*/)
       : __vptr(&Base::__vtable[0])  
       ...
     {
       ...
     }
    Now lets look at derived class. Suppose your C++ code defines class Der that inherits from class Base. The compiler repeats steps 1 and 3 (but not 2). In step 1, the compiler creates a hidden virtual-table, keeping the same function-pointers as in Base::__vtable but replacing those pointers that correspond to overrides. For instance, if Der overrides virt0() through virt2() and inherits the others as-is, Der's virtual-table might look something like this :)

    Code:
     FuncPtr Der::__vtable[5] = {
       &Der::virt0, &Der::virt1, &Der::virt2, &Base::virt3, &Base::virt4 //last 2 function ptrs inherited as it is
     };
    In step #3, the compiler adds a similar pointer-assignment at the beginning of each of Der's constructors. The idea is to change each Der object's virtual-pointer so it points at its class's virtual-table. This is not a 2nd virtual-pointer; it's the same virtual-pointer that was defined in the base class.

    Finally, let's see how the compiler implements a call to a virtual function:

    Code:
     // Your original C++ code
     
     void mycode(Base* p)
     {
       p->virt3();
     }
    The compiler has no idea whether this is going to call function Base::virt3() or function Der::virt3().It only knows for sure that you are calling virt3() which happens to be the function in slot 3 of the virtual-table.

    Hence, in this way the program can bind the function call dynamically to the correct function.

    Conclusion



    To Conclude, the concept of virtual function act as one of the pillars of the OOPs concept of polymorphism.
     
    tinytomhanks likes this.
  2. jyothishwebtech

    jyothishwebtech Banned

    Joined:
    Oct 7, 2011
    Messages:
    2
    Likes Received:
    0
    Trophy Points:
    0
    The above note gives the information about the virtual functions.The program written is give more idea about the virtual functions.
     
  3. dearvivekkumar

    dearvivekkumar New Member

    Joined:
    Feb 21, 2012
    Messages:
    29
    Likes Received:
    5
    Trophy Points:
    0
    Hi,

    Can you explain pictorially how the _vptr looks like in scenario below(in case of multiple inheritance). Or at-least the _vptr for Derieve class
    Code:
    class Base
    {
     public:
         virtual f1() {}
         virtual f2() {}
         virtual f3() {}
         virtual f4() {}
         virtual f5() {}
    };
    
    class Base1 : public Base
    {
    public:
        virtual f1() {}
    }
    
    class Base2 : public Base
    {
    public:
        virtual f1() {}
    };
    
    class D : public Base1, Base2
    {
    }; 
    
    Thanks
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice