C++ Virtual Function
C++ Virtual Function
A virtual function is a member function in base class that you expect to redefine in derived classes.
Before going into detail, let's build an intuition on why virtual functions are needed in the first place.
An Example to Begin With
Let us assume, we are working on a game (weapons specifically).
We created the Weapon class and derived two classes Bomb and Gun to load features of respective weapons.
#include <iostream>
using namespace std;
class Weapon
{
public:
void loadFeatures()
{ cout << "Loading weapon features.\n"; }
};
class Bomb : public Weapon
{
public:
void loadFeatures()
{ cout << "Loading bomb features.\n"; }
};
class Gun : public Weapon
{
public:
void loadFeatures()
{ cout << "Loading gun features.\n"; }
};
int main()
{
Weapon *w = new Weapon;
Bomb *b = new Bomb;
Gun *g = new Gun;
w->loadFeatures();
b->loadFeatures();
g->loadFeatures();
return 0;
}
Output
Loading weapon features. Loading bomb features. Loading gun features.
We defined three pointer objects w, b and g of classes Weapon, Bomb and Gun respectively. And, we called
loadFeatures()
member function of each objects using:w->loadFeatures(); b->loadFeatures(); g->loadFeatures();
Works perfectly!
However, our game project started getting bigger and bigger. And, we decided to create a separate Loader class to load weapon features.
This Loader class loads additional features of a weapon depending on which weapon is selected.
class Loader
{
public:
void loadFeatures(Weapon *weapon)
{
weapon->features();
}
};
The
loadFeatures()
loads the feature of a specific weapon.Let's try to implement our Loader class
#include <iostream>
using namespace std;
class Weapon
{
public:
- Weapon() { cout << "Loading weapon features.\n"; }
void features()
{ cout << "Loading weapon features.\n"; }
};
class Bomb : public Weapon
{
public:
void features()
{
this->Weapon::features();
cout << "Loading bomb features.\n";
}
};
class Gun : public Weapon
{
public:
void features()
{
this->Weapon::features();
cout << "Loading gun features.\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
Loading weapon features. Loading weapon features. Loading weapon features. Loading weapon features.
Our implementation seemed correct. However, weapon features was loaded 4 times. Why?
Initially, the Weapon object w is pointing to the b object (of Bomb) class. And, we tried to load the features of Bomb object by passing it to
loadFeatures()
function using l object to pointer (of Loader class).
Similarly, we tried to load the features of Gun object.
However, the
loadFeatures()
function of the Loader class takes pointer to object of a Weapon class as an argument:void loadFeatures(Weapon *weapon)
That's the reason weapon features are loaded 4 times. To solve this issue, we need to make function of base class (Weapon class) virtual using virtual keyword.
class Weapon
{
public:
virtual void features()
{ cout << "Loading weapon features.\n"; }
};
Example: Using Virtual Function to Solve the Problem
#include <iostream>
using namespace std;
class Weapon
{
public:
virtual void features()
{ cout << "Loading weapon features.\n"; }
};
class Bomb : public Weapon
{
public:
void features()
{
this->Weapon::features();cout << "Loading bomb features.\n";
}
};
class Gun : public Weapon
{
public:
void features()
{
this->Weapon::features();
cout << "Loading gun features.\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
Loading weapon features. Loading bomb features. Loading weapon features. Loading gun features.
Also, notice that, the
l->loadFeatures(w)
function calls the function of different classes depending upon what l object is pointing.
Using virtual function made our code not only clearer but flexible too.
In the above program, weapon features is printed twice. We encourage you to add additional code on the above program to load weapon features only once.
If we want to add another weapon (let's say knife), we can easily add and load features of it. How?
class Knife : public Weapon
{
public:
void features()
{
this->Weapon::features();
cout << "Loading knife features.\n";
}
};
And, in
main()
function.Knife k; w = &k; l->loadFeatures(w);
It's worth noticing that we didn't change anything in the Loader class to load features of knife.
C++ Abstract class and Pure virtual Function
The goal of object-oriented programming is to divide a complex problem into small sets. This helps understand and work with problem in an efficient way.
Sometimes, it's desirable to use inheritance just for the case of better visualization of the problem.
In C++, you can create an abstract class that cannot be instantiated (you cannot create object of that class). However, you can derive a class from it and instantiate object of the derived class.
Abstract classes are the base class which cannot be instantiated.
A class containing pure virtual function is known as abstract class.
Pure Virtual Function
A virtual function whose declaration ends 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.
Example: Abstract Class and Pure Virtual Function
#include <iostream>
using namespace std;
// Abstract class
class Shape
{
protected:
float l;
public:
void getData()
{
cin >> l;
}
// 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 << "Enter length to calculate the area of a square: ";
s.getData();
cout<<"Area of square: " << s.calculateArea();
cout<<"\nEnter radius to calculate the area of a circle: ";
c.getData();
cout << "Area of circle: " << c.calculateArea();
return 0;
}
Output
Enter length to calculate the area of a square: 4 Area of square: 16 Enter radius to calculate the area of a circle: 5 Area of circle: 78.5
In this program, pure virtual function
virtual float area() = 0;
is defined inside the Shape class.
One important thing to note is that, you should override the pure virtual function of the base class in the derived class. If you fail the override it, the derived class will become an abstract class as well.
Comments
Post a Comment