All About Virtual Functions in C++
In C++, a virtual function is a member function of a class that can be overridden by derived classes. It is declared as virtual in the base class and can be redefined by the derived class to provide a different implementation.
A member function declared in a base class and subsequently overridden by a subclass is known as a virtual function. This article will discuss virtual functions in C++ in-depth, along with the features and various sets of rules needed to work with virtual functions. We will also discuss a few examples to understand C++ virtual function and their working.
Explore Free Online Courses with Certificates
Table of Content
- Virtual Functions in C++
- Rules of Virtual Function in C++
- Virtual functions operating (VTABLE and VPTR)
- Late Binding or Dynamic Binding in C++
- Advantages of Virtual Functions
- Disadvantages
- Key point to remember
Explore Programming Courses
Best-suited C++ courses for you
Learn C++ with these high-rated online courses
What are Virtual Functions in C++?
The user can call a virtual function in C++ for an object of a derived class, and have it run the derived class’s version of the function when you refer to an object of a derived class to use a pointer or a link to the base class.
- No matter what kind of pointer or reference is used for the function call, virtual functions ensure that the right function is called for an object.
- Their primary purpose is to implement runtime polymorphism.
- In base classes, functions are declared using the virtual keyword.
- Runtime resolution of function calls is carried out.
Here is an example of a simple base class Shape with a virtual function draw():
class Shape {public: virtual void draw() = 0; // pure virtual function};
The draw() function is declared as virtual and pure virtual; this means it has to be overridden by any class that inherits from Shape.
Here is a simple example of a derived class Circle that overrides the draw() function of the base class:
class Circle : public Shape {public: void draw() { std::cout << "Drawing a circle" << std::endl; }};
Now if we have a pointer or reference of the base class type and pointing/referring to the object of the derived class Circle, calling the draw function will call the override implementation of the Circle.
Shape* s = new Circle;s->draw(); // will print "Drawing a circle"
This is the main advantage of virtual functions. It allows using a base class pointer or references to objects of different derived classes, and each derived class can provide its own implementation of the virtual functions.
It’s important to note that virtual functions have a small overhead associated with them due to the additional indirection required for dynamic binding. Sometimes, using non-virtual functions or function templates may be more appropriate.
Explore free C++ courses
Rules of Virtual Function in C++
The user needs to follow a basic set of rules to work with virtual functions in C++. Let us have a look at them below:
- Static operations cannot be virtual.
- A friend function present in a different class can be a virtual function.
- To accomplish runtime polymorphism, virtual functions should be accessed using a pointer or reference of the base class type.
- Virtual functions should have the same prototype in the base class and any derivations.
- It is always declared in the base class. They are overridden in derived classes. The function from the base class is used when the derived class does not explicitly override (or redefine) the virtual function.
- Virtual constructors are not permitted in classes, although virtual destructors are permitted.
- A virtual function must be declared as such in the base class using the “virtual” keyword. If a function is not declared as virtual, it cannot be overridden by derived classes.
- A virtual function in the base class must have a non-static implementation. A virtual function cannot be a pure virtual function (i.e. a function with no implementation and only declaration)
- A virtual function in the base class can have any access level (e.g. public, protected, private).
- A virtual function in the base class can have any return type, number, and type of arguments.
- A derived class can override a virtual function in the base class by providing a new implementation with the same function signature (i.e. same return type, function name and arguments).
- A derived class can choose not to override a virtual function in the base class, in which case the implementation in the base class will be used.
- A derived class cannot change the number or type of arguments of a virtual function; it must have the same signature as the base class’s virtual function.
- When a derived class overrides a virtual function, it can use the “override” keyword to indicate that the function is intended to override a virtual function in the base class and also for compile time check if the function has the correct function signature to override.
- A derived class can override a virtual function with a different access level, but it must be at least as accessible as the base class’s virtual function.
- A virtual function can be called through a pointer.
Let us consider the C++ code below:
class Parent { public: void print() { }};
class Child : public Parent { public: void print() { }};
The print() function of the Parent class is called if a pointer of Base type is later created to point to an object of a Derived class, and we call it.
To put it a simpler way, the Parent’s member function is not overridden.
int main() { Child Child1; Parent* Parent1 = &Child1; Parent1->print();
return 0;}
We use the virtual keyword to declare the print() function of the parent class as virtual to prevent this.
Let us see a simple C++ program to explain the Parent and child class in a virtual function:
#include <iostream>using namespace std;
class Parent { public: virtual void print() { cout << "It is a parent Function" << endl; }};
class Child : public Parent { public: void print() { cout << "It is a child Function" << endl; }};
int main() { Child child1;
Parent* parent1 = &child1; parent1->print(); return 0;}
The output of his C++ code will be:
It is a child Function.
Let us consider one more example to understand virtual functions:
#include <iostream> using namespace std; class M{ int e=50; public: void display() { std::cout << "The value of e is : " << e<<std::endl; } }; class N: public M { int f = 100; public: void display() { std::cout << "The value of f is : " <<f<< std::endl; } }; int main() { M *x; N y; x = &y; x->display(); return 0; }
The output of this code is:
The value of e is: 50
The base class class ‘M’ pointer is ‘e’ in the example. The pointer cannot access the derived class ‘N”s members; only the base class members may. The base pointer can point to any object descended from the base class in C++, but it is impossible to directly access the derived class members. Consequently, a virtual function that enables the base point to retrieve the properties of the derived class is required.
Virtual functions operating (VTABLE and VPTR)
As mentioned below, the compiler performs two tasks if a class has a virtual function.
A (VPTR) Virtual pointer is introduced as a class data member to point to the class’s VTABLE whenever an object of that class is created. A new virtual pointer is added as a class data member for each new object produced.
VTABLE, a static array of function pointers, is a member of the class regardless of whether an object is generated or not. The addresses of every virtual function found in that class are stored in the cells of this table.
Late Binding or Dynamic Binding in C++
In C++, “late binding” or “dynamic binding” refers to the mechanism by which a virtual function call is resolved at runtime rather than compile time. This means that the specific implementation of a virtual function that will be called is determined based on the actual type of the object rather than the type of the pointer or reference used to call the function.
When a virtual function is called on an object through a pointer or reference to the base class, the program looks up the virtual function table (vtable) of the object at runtime to determine the correct implementation of the function to call. The vtable is a data structure that stores a pointer to the virtual function for each class that has virtual functions.
This allows for polymorphism and flexibility in the code, as we can use a base class pointer or reference to objects of different derived classes. Each derived class can provide its own implementation of the virtual functions. This also allows us to create functions that can work with objects of different types without knowing the exact types at compile time.
For example:
#include
using namespace std;
class Shape { public: virtual void draw() = 0; };
class Circle : public Shape { public: void draw() { std::cout << "Drawing a Circle" << std::endl; } };
class Square : public Shape { public: void draw() { std::cout << "Drawing a Square" << std::endl; } };
int main(){ Shape* shapes[2] = { new Circle, new Square }; for (Shape* shape : shapes) { shape->draw(); //late binding } }
Output:
Drawing a CircleDrawing a Square
In the above example, we have an array of base class pointers pointing to objects of different derived classes. The draw() method is called on each object through the pointer. The program automatically chooses the correct implementation of the draw() method based on the actual type of the object at runtime.
Advantages of Virtual Functions in C++
In C++, virtual functions provide a number of advantages, including:
- Polymorphism: Virtual functions allow for polymorphism in the code, which means that objects of different types can be treated as objects of a common base class type. This allows for a more flexible and reusable code, as functions can be written to work with objects of different types without knowing the exact types at compile time.
- Code Reusability: Virtual functions make it easy to extend existing code by allowing new derived classes to reuse the base class code and provide their own implementation for specific functionality as needed.
- Simplified Maintenance: Virtual functions make it easier to maintain code by allowing changes to be made in a single location rather than multiple places throughout the code. This can make the code more readable, self-documenting, and maintainable.
- Late Binding: Virtual functions enable late binding, which means that the specific implementation of a virtual function that will be called is determined at runtime based on the actual type of the object rather than the type of the pointer or reference used to call the function.
- Dynamic Polymorphism: Virtual functions allow dynamic polymorphism. The program can use base class pointers or references to objects of different derived classes, and each derived class can provide its own implementation of the virtual functions.
- Polymorphic Behaviour: Virtual functions facilitate polymorphic behaviour; an operation may exhibit different behaviour in different instances of classes. This is achieved because virtual functions are resolved at runtime through the vtable.
- Interchangeability: Virtual functions interchange objects of different types, allowing them to be used interchangeably with objects of their base class.
Drawbacks of Virtual Functions in C++
While virtual functions provide a number of advantages in C++, they also have some drawbacks, including:
- Virtual functions introduce additional memory overhead due to storing the virtual function table (vtable) for each class with virtual functions. This can increase the program’s memory footprint, particularly for large programs with many classes.
- Virtual function calls also introduce some overhead at runtime. Because the program must look up the correct implementation of the function in the vtable before making the call. This can slow down the execution of the program, particularly in cases where virtual function calls are made frequently.
- Virtual functions can make the code more complex. The reason is it may be harder to understand the flow of the program when virtual function calls are used. This is particularly true when many derived classes are used, each with its own implementation of a virtual function.
- Virtual functions may cause issues with some optimization techniques, thus can decrease the performance.
- Virtual function calls can’t be in-lined as they are resolved at runtime through the vtable. Also, the compiler does not know which function to call.
- There is a difference in the order of constructor calls between non-virtual and virtual inheritance.
- The virtual mechanism causes the function call to take longer. It makes it harder for the compiler to optimize since it does not know precisely which function will be called at compile time.
- Virtual functions can make it slightly more challenging to determine where a function has been called from in complex systems.
Key Points to Remember
The derived class does not need to contain the virtual keyword. If a virtual function established in the base class is overridden. Also, functions are automatically regarded as virtual functions inside the derived class.
Conclusion
In this article, we have talked about C++ virtual function. A member function declared in a base class and subsequently overridden by a subclass is known as a virtual function. We have discussed various properties and rules to work with virtual functions in C++. This is all explained with the help of a few examples in C++.
Contributed by: Megha Chadha and Raju Kumar
This is a collection of insightful articles from domain experts in the fields of Cloud Computing, DevOps, AWS, Data Science, Machine Learning, AI, and Natural Language Processing. The range of topics caters to upski... Read Full Bio