English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
In this article, you will learn about virtual functions and their usage. In addition, you will also learn about pure virtual functions and abstract classes.
Virtual functions are member functions of the base class that you want to redefine in the derived class.
Before going into detail, let's first understand why virtual functions are needed first.
Let's assume that we are developing a game (for example, items: weapons).
We created the Weapon class and derived two classes, Bomb and Gun, which loaded the features of their respective weapons.
#include <iostream> using namespace std; class Weapon { public: void loadFeatures() { cout << "Load weapon features.\n"; } }; class Bomb : public Weapon { public: void loadFeatures() { cout << "Load the features of the knife.\n"; } }; class Gun : public Weapon { public: void loadFeatures() { cout << "Load the features of the gun.\n"; } }; int main() { Weapon *w = new Weapon; Bomb *b = new Bomb; Gun *g = new Gun; w->loadFeatures(); b->loadFeatures(); g->loadFeatures(); return 0; }
Output Results
Load weapon features. Load the characteristics of the knife. Load the features of the gun.
We defined three pointer objects w, b, and g for Weapon, Bomb, and Gun classes respectively. And, we used the following commands to call the loadFeatures() member function of each object:
w->loadFeatures(); b->loadFeatures(); g->loadFeatures();
Perfect work!
But, our game project is getting bigger and bigger. And, we decided to create a separate Loader class to load the weapon features.
This class Loader loads other features of the weapon based on the selected weapon.
class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } };
loadFeatures() loads the features of a specific weapon.
#include <iostream> using namespace std; class Weapon { public: Weapon() { cout << "Load weapon features.\n"; } void features() { cout << "Load weapon features.\n"; } }; class Bomb : public Weapon { public: void features() { this->Weapon::features(); cout << "Load the characteristics of the knife.\n"; } }; class Gun : public Weapon { public: void features() { this->Weapon::features(); cout << "Load the characteristics of the gun.\n"; } }; class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } }; int main() { Loader *l = new Loader; Weapon *w; Bomb b; Gun g; w = &b; l->loadFeatures(w); w = &g; l->loadFeatures(w); return 0; }
Output Results
Load weapon features. Load weapon features. Load weapon features. Load weapon features.
Our implementation seems to be correct. However, the loading of weapon features is loaded4times. Why?
Initially, the weapon object w points to the b object of the (Bomb) class. And we try to pass it to the loadFeatures() function using the l object pointing to the pointer of the (Loader class) to load the characteristics of the Bomb object.
Similarly, we try to load the characteristics of the Gun object.
However, the loadFeatures() function of the Loader class takes a pointer to a Weapon class object as a parameter:
void loadFeatures(Weapon *weapon)
That's why the weapon features are loaded4times. To solve this problem, we need to use the virtual keyword to implement the virtual function of the base class (Weapon class).
class Weapon { public: virtual void features() { cout << "Load weapon features.\n"; } };
#include <iostream> using namespace std; class Weapon { public: virtual void features() { cout << "Load weapon features.\n"; } }; class Bomb : public Weapon { public: void features() { this->Weapon::features(); cout << "Load the characteristics of the knife.\n"; } }; class Gun : public Weapon { public: void features() { this->Weapon::features(); cout << "Load the characteristics of the gun.\n"; } }; class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } }; int main() { Loader *l = new Loader; Weapon *w; Bomb b; Gun g; w = &b; l->loadFeatures(w); w = &g; l->loadFeatures(w); return 0; }
Output Results
Load weapon features. Load the characteristics of the knife. Load weapon features. Load the characteristics of the gun.
Additionally, note that l-The >loadFeatures(w) function calls different class functions based on the object pointed to by the l object.
Using virtual functions makes our code not only clearer but also more flexible.
In the above program, "Load weapon features." is printed twice. We suggest that you add other code to the above program to load weapon features only once.
If we want to add another weapon (such as bow), we can easily add and load its characteristics.How to add?
class Bow : public Weapon { public: void features() { this-<Weapon::features(); cout >> "Loading the characteristics of the bow.\n"; } };
And, add the following code in the main() function.
Bow b; w = &b; l->loadFeatures(w);
It is worth noting that we have not changed any content in the Loader class to load the characteristics of the knife.
The purpose of object-oriented programming is to divide a complex problem into several small sets. This helps to effectively understand and handle problems.
Sometimes, it is better to use inheritance only when it is better to visualize the problem.
In C ++In, you can create an abstract class that cannot be instantiated (you cannot create an object of this class). However, you can derive a class from it and instantiate an object of the derived class.
Abstract classes are base classes that cannot be instantiated.
A class that contains a pure virtual function is called an abstract class.
A virtual function ending with =0 is called a pure virtual function. For example,
class Weapon { public: virtual void features() = 0; };
Here, the pure virtual function is
virtual void features() = 0
And, the class Weapon is an abstract class.
#include <iostream> using namespace std; // Abstract class (a class that cannot be instantiated) class Shape { protected: float l; public: void getData() { cin >> l; } // Pure virtual function virtual float calculateArea() = 0; }; class Square : public Shape { public: float calculateArea() { return l*l; } }; class Circle : public Shape { public: float calculateArea() { return 3.14*l*l; } }; int main() { Square s; Circle c; cout << "Please enter the length to calculate the area of the square: "; s.getData(); cout << "The area of the square: " << s.calculateArea(); cout << "\nPlease enter the radius to calculate the area of the circle: "; c.getData(); cout << "Area of the circle: " << c.calculateArea(); return 0; }
Output Results
Enter the length to calculate the area of the square: 4 Area of the square: 16 Enter the radius to calculate the area of the circle: 5 Area of the circle: 78.5
In this program, the pure virtual function virtual float area() = 0; is defined in the Shape class.
One thing to note is that you should override the pure virtual function of the base class in the derived class. If the override fails, the derived class will also become an abstract class.