Click here to Skip to main content
15,885,853 members
Articles / Programming Languages / C++

Virtual Functions and their Implementations

Rate me:
Please Sign up or sign in to vote.
2.84/5 (20 votes)
6 Jul 2006CPOL3 min read 31K   18   3
Shows how virtual functions can be used

Introduction

Since I wrote my first article on CodeProject, I was eager to post the second one but, I didn't have the time to write down my thoughts and ideas. Now that I have an opportunity to write I am jotting down my ideas. The topic that I am going to discuss is at the beginner level but is always puzzling for an amateur developer (Virtual Functions). I would like to have comments and ideas on how I could improve on this article and I would be highly thankful if you can point out any mistakes and inform me about them.

So let’s start…

First of all, let’s understand what a virtual function is and why is it needed. A virtual function allows you to achieve one kind of polymorphism where you can inherit a class from a Base class and implement the function in the Derived class. Based on the type of object that is contained in the base class pointer, the compiler decides at runtime which function to invoke. (TOO MUCH ! Ah) Let’s go into a little bit of detail: Suppose we have a base class that has a virtual function called fun and a constructor. Now what? Derive two classes from this Base class and call them DerivedA, DerivedB. Implement the virtual function in both the classes. The code is as follows:

C++
#include "stdafx.h"
#include <iostream />
using namespace std;
class Base
{
public :
    Base()
    {
        cout<<"Constructor of Base"<<endl;
    }
    virtual void fun()
    {
        cout<<"In fun of Base class"<<endl;
    }
};
class DerivedA:public Base
{
public:
    DerivedA()
    {
        cout<<"Constructor of DerivedA"<<endl;
    }
    void fun()
    {
        cout<<"In fun of Derived A"<<endl;
    }
};
class DerivedB:public Base
{
public:
    DerivedB()
    {
        cout<<"Constructor of DerivedB"<<endl;
    }
    void fun()
    {
        cout<<"In fun of Derived B"<<endl;
    }

};
int _tmain(int argc, _TCHAR* argv[])
{
    Base b;
    DerivedA da;
    DerivedB db;
    return 0;
}

The output is:

Constructor of Base
Constructor of Base
Constructor of DervivedA
Constructor of Base
Constructor of DerivedB

Simple. Uptil now, we haven’t taken the virtual function and its implementation into account. We will do it now. Change the code of the main function to:

C++
Base b;
DerivedA da;
DerivedB db;
b.fun ();
da.fun();
db.fun();
b=da;
b.fun ();
b=db;
b.fun ();

The output will be shown as:

Constructor of Base
Constructor of Base
Constructor of DervivedA
Constructor of Base
Constructor of DerivedB
In fun of Base class
In fun of Derived A
In fun of Derived B
In fun of Base class
In fun of Base class

The last two lines of output are shocking for the amateur programmers. Since we have assigned the derived class object to the base class, the implemented function in the derived classes should have been called. But this does not happen. Instead a phenomenon known as Object Slicing takes place.

Object Slicing:

C++
#include "stdafx.h"
#include <iostream />
using namespace std;
class Base
{
    int i,j;
public :
    Base()
    {
        i=10;j=20;
        cout<<"Constructor of Base"<<endl;
    }
    virtual void fun()
    {
        cout<<"In fun of Base class"<<endl;
        cout<<i<<endl<<j;
    }
};
class DerivedA:public Base
{
    int k;
public:
    
    DerivedA()
    {
        k=30;
        cout<<"Constructor of DerivedA"<<endl;
    }
    void fun()
    {
        cout<<"In fun of Derived A"<<endl;
        cout<<k;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Base b;
    DerivedA d;
    b=d;
    b.fun ();

    return 0;
}

Output:

Constructor of Base
Constructor of Base
Constructor of DervivedA
In fun of Base class
10
20

So we see that inspite of a derived class object being assigned to a base class object, we were not able to get the value of k of the derived class. The object of the derived class is larger as compared with the object of the base class. In other words, we can say that the base class object knows nothing about k, i.e., the object has been sliced. Now let us see how can we call the implemented functions of the derived class:

C++
#include "stdafx.h"
#include <iostream>
using namespace std;
class Base
{
public :
    Base()
    {
        cout<<"Constructor of Base"<<endl;
    }
    virtual void fun()
    {
        cout<<"In fun of Base class"<<endl;
    }
};
class DerivedA:public Base
{
public:
    DerivedA()
    {
        cout<<"Constructor of DerivedA"<<endl;
    }
    void fun()
    {
        cout<<"In fun of Derived A"<<endl;
    }
};
class DerivedB:public Base
{
public:
    DerivedB()
    {
        cout<<"Constructor of DerivedB"<<endl;
    }
    void fun()
    {
        cout<<"In fun of Derived B"<<endl;
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
    Base *b=new Base();
    DerivedA da;
    DerivedB db;
    b->fun ();
    b=&da;
    b->fun();
    b=&db;
    b->fun ();
    return 0;
}

Output:

Constructor of Base
Constructor of Base
Constructor of DervivedA
Constructor of Base
Constructor of DerivedB
In fun of Base class
In fun of Derived A
In fun of Derived B

The other day I was asked a question about which of the two statements would work?

  1. Base *b=new DerivedA();
  2. Derived *d=new Base();

I implicitly answered the first one would work and the second one would fail as a pointer of Derived class could not be typecasted to that of the base class implicitly. To make this thing work, we would have to write:

C++
Base *b=new DerivedA(); 
DerivedA *d=reinterpret_cast<deriveda* />(new Base()); 

To see the change of how the functions are called, change the code in the main function of the above example to:

C++
Base *b=new DerivedA();
DerivedA *d=reinterpret_cast<DerivedA*>(new Base());
b->fun();
d->fun();

Output:

Constructor of Base
Constructor of DervivedA
Constructor of Base
In fun of DerivedA
In fun of Base class

Let’s do everything once with pointers:

C++
#include "stdafx.h"
#include <iostream />
using namespace std;
class Base
{
public :
    Base()
    {
        cout<<"Constructor of Base"<<endl;
    }
    virtual void fun()
    {
        cout<<"In fun of Base class"<<endl;
    }
};
class DerivedA:public Base
{
public:
    DerivedA()
    {
        cout<<"Constructor of DerivedA"<<endl;
    }
    void fun()
    {
        cout<<"In fun of Derived A"<<endl;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Base *b=new Base();
    DerivedA *da=new DerivedA();
    b->fun ();
    da->fun();
    b=da;
    b->fun ();

    return 0;
}

Output:

Constructor of Base
Constructor of Base
Constructor of DervivedA
In fun of Base class
In fun of DerivedA
In fun of DerivedA

After having discussed so much about virtual functions, we haven’t even mentioned about VPTR and the VTABLE. Oh! This topic is endless… So here’s a description of VPTR and VTABLE for you. What’s a vptr ? The vptr is a pointer which is the very data member of a class and can be accessed using the this pointer. ( I will just show this in a while). Vptr holds the address of the virtual table of the class thereby associating an object with virtual table. There is always a VTABLE created for a class having a virtual function and each of the classes that are derived from it. Where does the vptr reside and how can we invoke a function using this pointer? Now it’s the time to fulfill my promises that I made earlier in the article.

C++
class Base
{
public:
    virtual void __cdecl fun(int q)
    {
        cout<<"Hello Base : "<<endl<<q<<endl;
    }
    void CallVirtualFnUsingThis()
    {
        int *p=(int *)this;
        cout<<"Address of Object : "<<*p<<endl;
        p=(int *)*p;
        cout<<"Address of VPTR : "<<p<<endl;
        p=(int *)*p;
        cout<<"Address of First Virtual Function in VTABLE : "<<p<<endl;
        void (__cdecl *pfun)(Base* const,int);
        pfun=(void(__cdecl*)(Base* const,int))p;
        (*pfun)(this,25);
    }
};     

int _tmain(int argc, _TCHAR* argv[])
{
    Base b;
    b.CallVirtualFnUsingThis();
    return 0;
}

And the output:

C++
// This may differ from the actual object on your machine
Address of Object : 4514088
Address of VPTR : 0044E128
Address of First Virtual Function in VTABLE :0041A0D6
Hello Base :
25

Comments

Any suggestions for the improvement of this article are most welcome. I would appreciate if you could read this article and rate it. That's all for now. I will update this article to add some images for showing how vptr and vtable are shown in memory.

History

  • 6th July, 2006: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
India India
He's been programming with C/C++ as the main stream(VC++(6.0/7.1),WIN32,MFC,COM,ATL). Prior to this he was working on Java and did a lot of work in Java-Swing and JSP. What makes him stand apart is his passion for work.
He helps those who seek for it.
Loves to work in team or as an individual and always has a word of advice or two.
The following is what he says about "What an individual should do to SUCCEED?"
One must analyze oneself. It is one of those virtues that encourages an individual to perform well and succeed.
This is a Organisation (No members)


Comments and Discussions

 
Generalvptr Pin
Member 260584311-Sep-08 0:40
Member 260584311-Sep-08 0:40 
GeneralMultiple inheritance, virtual bases Pin
Pavel Vozenilek8-Jul-06 0:13
Pavel Vozenilek8-Jul-06 0:13 
The code in the article may fail for multiple inheritance. Having virtual base class makes the situation even more complicated. A superoptimizing compiler may detect cases where virtualness is not used and eliminate them. The compiler isn't required to put VTBL pointer as the first member and more, using virtual table isn't mandated by standard.

However typical compilers and classes limited to single inheritance do as the article presents.
GeneralNice Pin
coolmandan7-Jul-06 17:31
coolmandan7-Jul-06 17:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.