Difference between calling of virtual function and non virtual function?

17,064

Solution 1

Though virtualism/dynamic dispatch is strictly implementation defined, most(read all known) compilers implement it by using vptr and vtable.

Having said that, the difference between calling a non virtual function and virtual function is:

Non-virtual functions are resolved statically at Compile-time, While Virtual functions are resolved dynamically at Run-time.

In order to achieve this flexibility of being able to decide which function to call at run-time, there is an little overhead in case of virtual functions.

An additional fetch call that needs to be performed and it is the overhead/price you pay for using dynamic dispatch.

In case of non-virtual function the sequence of calls is:

fetch-call

The compiler needs to fetch address of the function and then call it.

While in case of virtual functions the sequence is:

fetch-fetch-call

The compiler needs to fetch the vptr from the this, then fetch the address of the function from the vptr and then call the function.

This is just a simplified explanation the actual sequence maybe far more complex than this but this is what you really need to know, One does not really need to know the implementation nitty gritty's.

Good Read:

Inheritance & Virtual Functions

Solution 2

If you have a base class 'Base' and derived class 'Derived' and you have a function 'func()' defined as virtual in Base class. This func is overridden by the Derived class.

Suppose you define

       Base obj = new Derived();
       obj.func();

Then the 'func' of Derived class is called. While if 'func()' was not defined as virtual in Base then it would be called from 'Base' class. This is the difference how the function calling differs for vitual and non-virtual functions

Solution 3

Non-virtual member functions are resolved statically. member function are binding statically at compile-time based on the type of the pointer (or reference) to the object.

In contrast, virtual member functions are binding dynamically at run-time. If class have atleast one virtual member function then compiler puts a hidden pointer in the object called a vptr(virtual table address) during the construction of object.

The compiler creates a v-table for each class that has at least one virtual function. Virtual table contain virtual function's address. It can be array or list(depend upon the compiler) of virtual function pointer's
During a dispatch of a virtual function, the run-time system follows the object’s v-pointer(fetch the address from class object) to the class’s v-table, then offset is added to the base address(vptr) and call the function.

The space-cost overhead of the above technique is nominal: an extra pointer per object (but only for objects that will need to do dynamic binding), plus an extra pointer per method (but only for virtual methods). The time-cost overhead is also fairly nominal: compared to a normal function call, a virtual function call requires two extra fetches (one to get the value of the v-pointer, a second to get the address of the method).

None of this runtime activity happens with non-virtual functions, since the compiler resolves non-virtual functions exclusively at compile-time based on the type of the pointer.

I have taken simple example to understand in better manner, how the binding happened for non virtual function & virtual function and how virtual function mechanism works.

#include<iostream>
using namespace std;
class Base
{
        public:
                virtual void fun()
                {}
                virtual void fun1()
                {}

                void get()
                {
                        cout<<"Base::get"<<endl;
                }
                void get1()
                {
                        cout<<"Base::get1"<<endl;
                }
};

class Derived :public Base
{
        public:
                void fun()
                {
                }
                virtual void fun3(){}
                void get()
                {
                        cout<<"Derived::get"<<endl;
                }
                void get1()
                {
                        cout<<"Derived::get1"<<endl;
                }

};
int main()
{
    Base *obj = new Derived();
    obj->fun();
    obj->get();
}

How the vtable created for Base & derived class

Assembly code is generated for better understanding.

$ g++ virtual.cpp -S -o virtual.s

I have fetched the information of vtable from virtual.s for Base and Derived class respectively:

_ZTV4Base:
        .quad   _ZN4Base3funEv
        .quad   _ZN4Base4fun1Ev
_ZTV7Derived:
        .quad   _ZN7Derived3funEv
        .quad   _ZN4Base4fun1Ev
        .quad   _ZN7Derived4fun3Ev

As you can see fun & fun1 are only two virtual functions in Base class. Vtable of Base class(_ZTV4Base) have entries of both virtual functions. Vtable does not have entry of non-virtual function. Please do not confuse with name of fun(ZN4Base3funEv) & fun1(ZN4Base4fun1Ev), their name got mangled.

Derived class vtable have tree entries

  1. fun(_ZN7Derived3funEv) override function
  2. fun1(_ZN4Base4fun1Ev) inherited from Base class
  3. fun3(_ZN7Derived4fun3Ev) new function in derived class

How non virtual function & virtual function called?

for non-virtual function

    Derived d1;
    d1.get();

    subq    $16, %rsp
    leaq    -16(%rbp), %rax
    movq    %rax, %rdi
    call    _ZN7DerivedC1Ev //call constructor
    leaq    -16(%rbp), %rax
    movq    %rax, %rdi
    call    _ZN7Derived3getEv //call get function

Simply tell, fetch and call get(binding happened at compile time)

for non-virtual function

Base *obj = new Derived();
obj->fun();
pushq   %rbx
subq    $24, %rsp
movl    $8, %edi
call    _Znwm   //call new to allocate memory 
movq    %rax, %rbx
movq    $0, (%rbx)
movq    %rbx, %rdi
call    _ZN7DerivedC1Ev //call constructor
movq    %rbx, -24(%rbp)
movq    -24(%rbp), %rax
movq    (%rax), %rax
movq    (%rax), %rax
movq    -24(%rbp), %rdx
movq    %rdx, %rdi
call    *%rax //call fun

fetch the vptr, add the function offset, call the function(binding happened at run time)

Assembly of 64 is confusing most of c++ programmer but if any would like to discuss then welcome

Solution 4

The overhead of calling a virtual method is significant.

Also this.

Solution 5

When calling a virtual method, it has to look up which function to call in a virtual function table.

Share:
17,064

Related videos on Youtube

cheng
Author by

cheng

Updated on June 08, 2022

Comments

  • cheng
    cheng about 2 years

    This is in fact an interview question, I can't figure out the answer. Anyone knows about this? You can talk about any difference, for example, the data that are push into stack.

    • GManNickG
      GManNickG over 12 years
      A virtual function undergoes dynamic dispatch. You should pick a good C++ book to learn it better.
  • cheng
    cheng over 12 years
    So what is the difference between calling a virtual function and non-virtual function? For example, how much memory access are necessary for each of them?
  • icktoofay
    icktoofay over 12 years
    @cheng: I don't know how much more accesses virtual method calls have than non-virtual method calls have (I'd assume one or two), but virtual method calls do require a little more work.
  • cheng
    cheng over 12 years
    Can you provide more details about the process of fetch-call and fetch-fetch-call? Or can you provide some readings? Thanks.
  • cheng
    cheng over 12 years
    OK, I will try to figure this out. Thank you.
  • Alok Save
    Alok Save over 12 years
    @cheng: Added a link, which provides a good explanation of the details.
  • Alok Save
    Alok Save over 12 years
    A few subtle points: 1. obj needs to be a pointer. 2. Apt to mention that the overidding function Derived::func() should take the same arguments to be overidding the Base::func()
  • cheng
    cheng over 12 years
    The accepted answer provide a good reading about this problem. See above.
  • Kerrek SB
    Kerrek SB over 12 years
    It might be worth considering the effect on caching and pipelining and memory locality that the extra level of indirection has. What looks simple on paper may have non-negligible effects in practice.
  • MSN
    MSN over 12 years
    The sequence for a non-virtual function is call. The sequence for a function pointer is fetch-call. A vtable adds another fetch.
  • 463035818_is_not_a_number
    463035818_is_not_a_number about 9 years
    It is not a subtle point, but a crucial one. "obj.func()" will not call the base class method but one has to use pointers and call "obj->func()" to make use of virtual methods.
  • Ajay yadav
    Ajay yadav over 8 years
    Hi Alok, Just one improvement to the answer, Compiler fetch the vptr from object instead of this. this coming to the picture after calling the member function
  • curiousguy
    curiousguy over 8 years
    Calling a normal function in a dynamically linked module might involve pointer indirection.
  • Alan
    Alan almost 8 years
    Note that one can use references rather than pointers, though not always in the same situations. For instance, a reference cannot be rebound to refer to a different object.