Thomas Sampson

Understanding the virtual keyword in C++

The virtual keyword is used to allow for true polymorphism to take place in your c++ application.

A real world example

Let’s create a situation which requires both a base and specialised (derived) class architecture. We will use the real world example of Transport and Car, where Car is derived from transport (as would be a bike, or a plane for that matter). Here are the defenitions for each object

The Transport Object

class Transport

{

     private:

        int topSpeed;

     public:

        void IdentifyMyself()

        {
             cout << "I am a Transport Object";
        }

};

The Car Object

class Car : public Transport

{

     private:

     public:

     void IdentifyMyself()

     {

          cout << "I am a Car Object" ;

     }

};

Simple enough?

So, lets examine what actually goes on here

1. The Transport class is created as our base class.

2. The Transport class has one private data member.

3. The Transport class has one public method named IdentifyMyself() which outputs a message.

4. The Car object is created as our specialised/derived class.

5. The Car publicly inherits from the base class Transport, obtaining its private data member topSpeed.

6. The Car also defines a public method named IdentifyMyself() which also outputs a message.

What next?

The important part to take from above is that Car inherits from Transport, and that they both contain a defenition for the method IdentifyMyself().

So let’s use our objects! Here is a snippet of code which uses our objects and their methods…

Transport t;

Car c;

t.IdentifyMyself();

c.IdentifyMyself();

Output

I am a Transport ObjectI am a Car object

So, the Car’s method overrides the base class method and the two objects print a different message, simple. Now consider the following….

Test code

Transport* t

Car c;

t=&c;

c.IdentifyMyself();

t->IdentifyMyself();

We now have a car object as before, but this time we setup a pointer “t” (of the type Transport) to point to our Car, “c” . We are allowed to do so as “Car is a type of Transport”. However, take note of the output.

Output

I am a Car objectI am a Transport object

Even though our pointer “t” points to a Car object, we still call the IdentifyMyself() method of the base class. This is because there is no overriding, and as the pointer is of the type Transport, we simply call the Transport’s method, no longer the one overridden by Car. This is where the virtual keyword comes into play.

Let’s redefine the base class Transport to unclude a virtual method…

The New Transport Object

class Transport

{

     private:

        int topSpeed;

     public:

        virtual void IdentifyMyself()

        {
             cout << "I am a Transport Object";
        }

};

Now lets run the same code again and notice the difference in output….

Test code

Transport* t

Car c;

t=&c;

c.IdentifyMyself();

t->IdentifyMyself();

Output

I am a Car objectI am a Car object

This time when we dereference the pointer “t”,  even though our pointer is of the type Transport, due to Transport using the virtual keyword before IdentifyMyself(), it now forces the program to downcast to our inherited object Car and call the overridden IdentifyMyself() method instead.

How does this work under the hood?

Let’s think back to when our Transport class  did not employ the virtual keyword. At compile time we can collect up all Transport’s class methods and know where they will reside in memory. However, when we use the virtual keyword, when accessing a pointer to a Transport object, these methods could be anywhere in memory (depending upon the layout of the derived type which overrides them).

The way to resolve this is for each object which uses virtual methods, to keep a vtable (virtual method lookup table) which keeps a list of virtual function pointers. This vtable is always kept at memory offset 0 and can be though of as a hidden datamember for classes which use the virtual keyword or override virtual methods. This can be seen when watching a pointer to our Transport instance, using the Visual Studio Debugger as shown below as __vfptr (virtual function pointers).

This is done automatically by the compiler for any base class which employ virtual methods, and inherently for those that derive from the base class. This vtable is always kept at memory offset 0, so we can always take a pointer to a Transport object, and check its vtable and call the right method. This becomes more complex with multiple inheritence.

Summary

Imagine increasing the scale of this program to include Planes, Trains and Motorbikes. We could now keep a list, 100 items long, of transport pointers…

Transport* mylist[100];

…and iterate through this list, calling ->IdentifyMyself() on each item. Now that the base class implements the virtual keyword, true polymorphism takes place and any derived classes which choose to override this method, get their chance to output a custom message. For those classes that dont override the IdentifyMyself() method, the base class method will be called.

Advertisements

Author: tomtech999

I have recently graduated with a 1st class degree in MComp Games Software Development at Sheffield Hallam University, focusing primarily on application development in C++, with experience in graphics programming, scripting languages, DVCS/VCS and web technology. In my spare time I enjoy Drumming, Reading and Snowboarding!

Comments are closed.