Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C++ (http://www.go4expert.com/articles/cpp-tutorials/)
-   -   Virtual Functions in C++ Part II (http://www.go4expert.com/articles/virtual-functions-cpp-part-ii-t29388/)

Trinity 30Dec2012 13:38

Virtual Functions in C++ Part II
 
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.


All times are GMT +5.5. The time now is 04:37.