Virtual Function in C++

Video Tutorial
FREE
Functions thumbnail
This video belongs to
C++ Course: Learn the Essentials
14 modules
Certificate
Topics Covered

Overview

A virtual function is a member function which is declared within a base class and is overridden by a derived class. Now, when you refer to a derived class object using a pointer to the base class, you can call a virtual function for that object and execute the derived class’s version of that particular function.

Introduction to Virtual Functions in C++

Imagine you are the head of a futuristic huge Avengers robots factory.

Your factory makes robot toys of every Avenger out there – be it Hulk, Thor, Iron Man, or Black Widow.

Now these robots need system chips to work.

Whenever a kid exclaims the words “Avenger Attack!”, these chips have the purpose to determine what unique attack the Avenger has.

If the kid exclaims “Avenger Attack!” to Thor, then it would start dazzling with thunder like lights.

On the other hand if you exclaim “Avenger Attack!” to Iron Man, then a lot of cool miniguns shall come out of it.

Now, there are two ways to go about making these chips.

First, you create a unique chip for each unique Avenger. Each chip has its own logic written when the word “Avenger Attack!” is spoken. That’s wayy too much work.

Second, you create a single smart chip that determines in real time – what special attack the Avenger has & executes the relevant logic written in the chip.

Essentially then, the work boils down to creating one chip and putting that in every Avenger robot made in your factory.

Your factory’s production speed simply gets supercharged!

The second way is similar to what is known as the concept of virtual functions in C++. Let’s dive deeper into that concept in the C++ language.

Instead of cool robots here, think of Avenger as a “parent” class in C++, & our beloved Iron Man as a “child class” in C++. That’s a simple case of what we refer to as inheritance in C++ classes.

A function is defined in the Avenger class and is named as introduce().

A function, with the same name introduce() and with the same function signature is defined in the Iron Man class as well.

Quick Note: If two functions accept the same input (input variables are same in number as well as its data-types) and return the same output type, they are said to bear identical function signatures.

A call to Avenger’s introduce() function prints a very dull response of “Hi, I am an Avenger”. While a call to Iron Man’s introduce() function has the output response of “Hey, I am Iron Man. Genius, Billionaire, Playboy, Philanthropist”.

Cooler response, for sure

A peculiar behaviour now arises due to the way C++ function binding works during compile time, something we are about to explore in the next few paragraphs.

If you create a pointer of type Avenger in C++ code, and set it to point to an object of type Iron Man then any call made to the function introduce() during run-time will simply print the dull “Hi, I am Avenger” response.

In other words, a function call to introduce() during run-time here doesn’t invoke the introduce() function defined in Iron Man class. This is despite the fact that the avenger pointer is pointing to an object of type Iron Man.

You might have a few questions here.

Why not simply create a pointer of type Iron Man here, & set it to point to an Iron Man object to solve this problem, even if it’s there.

End of discussion that would be. Plain and simple.

True, one could always do that. But there are certain use-cases in programming where you would simply like to create, say, an array of parent class pointers; and then conveniently assign each of these pointers to any one of its derived classes.

There’s a huge lot of convenience in doing so.

If you come to think of it – model a situation where there is 1 parent class and say, 20 child classes inheriting from it.

While you are deeply focused in writing some C++ code, you don’t have to worry about creating multiple unique pointers (that’s 21 unique pointers, even if you create one for each class here).

You can simply create multiple pointers of the same type, the parent class, and supposedly store them in an array. These pointers can then be conveniently assigned to point to each of the 21 different objects it supports.

The ingenuity of this convenience is upheld by introducing virtual functions.

But as Richard Parker once said, with great power comes great responsibility 😉

Let’s get back to our example to explore the responsibility that befalls us here.

In order to ensure that a run-time call to introduce() functions calls the introduce() function defined in the Iron Man class in our example above, we are responsible to set the introduce() function in the parent Avenger class as “virtual”.

This in turn prevents what we referred to as a peculiar problem above – the problem of what is called “early binding”.

Early binding is done by online C++ compiler, when C++ code is being compiled.

Consider the same C++ code snippet we discussed above:

You might be aware that each variable declared in C++ resides in some memory location in the system. These variable addresses are only assigned by the environment (the OS or operating system) in run-time, and not during compile-time.

In the C++ code snippet above, the memory addresses of avenger pointer and of ironMan object are therefore only resolved during run-time.

Hence the C++ compiler is only aware of the type of the pointer during compile time. That’s the only information it has to work with.

Working with just this information, the compiler binds the pointer’s introduce() function call in our C++ code snippet above, to the matching introduce() function defined in the Avenger class.

Quick Note: By matching we imply having same function name & signature.

It ignores the fact that the pointer consists of the address of an IronMan class object, since addresses haven’t been assigned yet.

In other words, the compiler simply takes a peek at the class the pointer belongs to, and binds the function call with the matching function defined in that C++ class.

But, how do we circumvent that?

You circumvent early binding by declaring the introduce() function as virtual in the Avenger class.

This essentially signals the compiler to not to resort to early binding, & exhibit a behaviour known as late binding.

We shall cover more on how that’s done later in the article.

Rules of Virtual Function in C++

  1. Virtual function in C++ must be members of some class. This simply implies that they must be defined inside a class in C++.

  2. Virtual function in c++ cannot be static members of the class. Hence, virtual functions belong to the objects of the class – and not the class itself. Remember, members of type static are created only once – but non-static members are created each time when a new object of the class is spawned.

  3. Building on the second point, virtual functions must only be accessible via object pointers. Access to virtual functions via class names must be prohibited.

  4. A Virtual Function in C++ can be created as a friend function of some other class. A friend function is a specialised function in C++ that’s empowered to access the private and protected class members of the class it is “friends” with.

  5. Virtual functions must be defined in the base class. Functions once defined as virtual in the base class need not be marked as virtual in derived classes. Hence, the demarcation of functions as virtual is done at base class level.

  6. The function prototype of a function designated as a virtual function must be similar in the base class and all the derived classes.

Quick Tip: Similar Function prototype = Similar Function Name + Similar Function signature

The risk of not following this would be the compiler actually treating the functions as overloaded, and not virtual.

Example of Virtual Function in C++

Output:

Thor Attack!

Compile-Time Polymorphism vs Run-Time Polymorphism

Let’s understand the run-time behaviour of virtual functions via the following code example.

Output:

The above output reflects our discussion on Avengers robots above.

We created a pointer of type Avenger, and assigned its value as the address of BlackWidow object.

Now, when the virtual function avengerAttack() is invoked at runtime, the compiler binds the call to the avengerAttack() function defined in the child class BlackWidow.

Since this is done at run time, it is also referred to as run-time polymorphism or late binding.

An instance of compile time polymorphism can be observed when showVictorySign() function is invoked.

Since it hasn’t been marked as virtual, the base class version of showVictorySign() gets executed.

Working of Virtual Function in C++

When a class consists of a virtual function, the C++ compiler responds by performing below two actions:

A. Every new object created of a class with at least one virtual function defined in it consists of a virtual pointer (VPTR).

This virtual pointer is inserted into the newly created object by the C++ compiler, as one of the object’s data members. This virtual pointer points to VTABLE, covered in point (b).

Remember, in case of C++ class inheritance, all data members and functions of the base class are accessible by the child classes.

Riding on that concept, the C++ compiler only inserts the VPTR into the parent class during the process of object creation, irrespective of whether an object of the parent or any of the child classes is being created. Inserting the VPTR pointer in the parent class makes the VPTR pointer variable accessible across objects of both parent and child classes. That’s one neat trick by the C++ compiler!

B. At the class level, there exists a static data member, namely VTABLE.

It belongs to the class, & not its objects – and is modeled as a static array consisting of function pointers.

Just like object pointers point to the objects, function pointers point to functions.

Each array bucket (cell) of VTABLE consisting of a function pointer, points to some virtual function in the class. The number of buckets (cells) in the array is simply equal to the number of virtual functions defined & available in the class.

The construct of VTABLE is eventually used to achieve late binding (runtime polymorphism)

C++ Override Identifier

From C++ 11 onwards a new identifier override has been provided, with a view to avoid bugs while using virtual functions.

Override identifier specifies the member functions of the parent class that override the member function of the child class.

If we use a function prototype in parent class and define that function outside of the class, then we use the following code:

Use of C++ Override Identifier

There is always a possibility to make blunders/mistakes while declaring member functions of the parent classes, when using virtual functions.

The override identifier asks the compiler to display error messages when these blunders are made.

In the absence of these error flags/messages, even though the program might compile, there’s a possibility that the virtual function does not get overridden.

List of some of possible mistakes are:

A. Incorrect function names. For example, if the virtual function in the parent class is named avengerAttack(), but we accidentally name the overriding function in the child class as avengeAttack().

B. Non-matching function parameters.

C. No function is defined as virtual in the parent class

Use of Virtual Function in C++

Time to use our helpful Avenger buddies to understand the use of C++ Virtual functions!

We now create two public functions for our Avenger robots.

  1. avengerAttack() to define the type of special attack assigned to the robot.
  2. sayYourName() to make the robot introduce themselves.

In order to keep our program clean, we use the concept of virtual functions.

Pure Virtual Function in C++

A pure virtual function’s usage is not intended to perform any task.

It sits as a placeholder.

A virtual function with no function definition is called a “do-nothing” or a pure virtual function.

A pure virtual function is defined in the parent class and has no definition relative to the parent class.

The parent class here, which consists of the pure virtual function, are known as abstract base classes.

Objects of abstract base classes can’t be instantiated.

Pure virtual function can be defined as:

Example of Pure Virtual Function in C++

Output:

Conclusion

In this article, we learned:

  1. Concept of virtual functions
  2. Rules of virtual functions in C++
  3. Compile-time polymorphism vs Run-time polymorphism
  4. Working of virtual function in C++
  5. C++ override identifier
  6. Uses of C++ override identifier
  7. Pure virtual functions in C++

That’s all for now folks!

Thanks for reading.

See Also: