Virtual Functions in C++ Part II

Discussion in 'C++' started by Trinity, Dec 30, 2012.

  1. Trinity

    Trinity New Member

    Joined:
    Nov 23, 2011
    Messages:
    28
    Likes Received:
    11
    Trophy Points:
    3
    Occupation:
    Software Engineer
    In part I of the article about Virtual Functions, we learnt about the virtual functions, why are they needed and when to use them. This article will talk about virtual function internals and how virtual functions work in C++. Actually, virtual functions govern to an extent, how a class object is laid in the memory. We shall be learning more in further sections.

    Looking back at the Behavior



    Let us brush up the behavior of virtual functions, which is very essential to understand before we jump onto its working. We have a base class and a derived class. The base class has a virtual function defined, and the derived class has the same function definition overridden.

    If we have a base class pointer, there are two possible cases.
    • case 1: The pointer points to an object of type base class. In this case, the function call would call the function definition from the base class.
    • case 2: The pointer points to an object of the type derived class. Here, the function call would call the function definition from the derived class.

    How virtual functions work?



    When a class has virtual function or virtual functions, the particular class will hold a virtual table, also called the v-table. Moreover, compiler adds a pointer, called a virtual pointer, to this v-table in the memory allocation. This would be hidden and not directly accessible though.

    What does this v-table stores? Well, it stores the addresses of the virtual functions in the base class. Not only the base class, even the derived classes with virtual functions in their parent class, also hold a v-table. In the derived classes, if the derived class overrides the of virtual class of the parent class, then v-table contains the address of the function defined in the derived class. In case, the derived class does not override the virtual function of the parent class, derived class v-table contains the address of the virtual function definition of the parent class. This is how, dynamic polymorphism works in C++ using virtual tables.

    Virtual Destructors



    A destructor of the base class needs to be declared as virtual. Even the virtual destructors have the same behavior as that of virtual functions. However, it has an additional feature. When a derived class object goes out of scope, the destructor of the derived class is called followed by the call to the base class destructor.

    Hence, combining both the features in circumstances where we have a base class pointer, pointing to a derived class. Now, when this pointer goes out of scope, it would call the base class destructor only, which would lead to a discrepancy. This discrepancy can be resolved by declaring the base class destructor as virtual. And it also explains the reason as to why is it needed.

    Let us see the same circumstance in implementation.
    Code:
    
    #include<iostream>
    
    using namespace std;
    class Base
    {
    public:
    
        void display()
        {
            cout << "Base:display function\n";
        }
    
        ~Base()
        {
            cout<<"Base destructor\n";
        }
    };
    
    class Derived : public  Base
    {
    public:
        ~Derived()
        {
            cout<<"Derived destructor\n";
        }
    };
    
    int main()
    {
        Base *bp = NULL;
        {
            bp = new Base();
            delete bp;
        }
        {
            bp = new Derived();
            delete bp;
        }
       return 0;
    }
    
    When we compile and run the above cpp program source, we see
    Code:
    ubuntu@ubuntu-VirtualBox:~$ g++ main.cpp -o main
    ubuntu@ubuntu-VirtualBox:~$ ./main
    Base destructor
    Base destructor
    
    See, in both the cases, only the base destructor is called. This could be a critical problem, when there are lots of memory allocations in the derived class, and a lot more cleaning up needs to be done in the destructor. Here, it will skip calling the derived destructor, and leading to fatal unexpected behaviors skipping the cleanup as well.

    We resolved this problem by declaring the base destructor as virtual as follows

    Code:
    
    #include<iostream>
    
    using namespace std;
    class Base
    {
    public:
    
        void display()
        {
            cout << "Base:display function\n";
        }
    
        virtual ~Base()
        {
            cout<<"Base destructor\n";
        }
    };
    
    class Derived : public  Base
    {
    public:
        ~Derived()
        {
            cout<<"Derived destructor\n";
        }
    };
    
    int main()
    {
        Base *bp = NULL;
        {
            bp = new Base();
            delete bp;
        }
        {
            bp = new Derived();
            delete bp;
        }
       return 0;
    }
    
    Now, when we compile and run,
    Code:
    ubuntu@ubuntu-VirtualBox:~$ g++ main.cpp -o main
    ubuntu@ubuntu-VirtualBox:~$ ./main
    Base destructor
    Derived destructor
    Base destructor
    
    This is the exact behavior of destructors which is expected.
     

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