原文: https://www.programiz.com/cpp-programming/virtual-functions
虚函数是您希望在派生类中重新定义的基类中的成员函数。
在详细介绍之前,让我们先了解一下为什么首先需要虚拟函数。
让我们假设,我们正在开发一个游戏(特别是武器)。
我们创建了Weapon类,并派生了两个类Bomb和Gun以加载相应武器的特性。
#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;
}输出
Loading weapon features.
Loading bomb features.
Loading gun features.我们定义了Weapon,Bomb和Gun类的三个指针对象w,b和g。 并且,我们使用以下命令调用了每个对象的loadFeatures()成员函数:
w->loadFeatures();
b->loadFeatures();
g->loadFeatures();完美的作品!
但是,我们的游戏项目开始变得越来越大。 并且,我们决定创建一个单独的Loader类来加载武器特性。
此类Loader类根据选择的武器来加载武器的其他函数。
class Loader
{
public:
void loadFeatures(Weapon *weapon)
{
weapon->features();
}
}; loadFeatures()加载特定武器的特性。
#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;
}输出
Loading weapon features.
Loading weapon features.
Loading weapon features.
Loading weapon features. 我们的实现似乎是正确的。 但是,武器特性被加载了 4 次。 为什么?
最初,武器对象w指向Bomb类的b对象。 并且,我们尝试使用指向Loader类的指针的l对象将其传递给loadFeatures()函数来加载Bomb对象的特性。
同样,我们尝试加载Gun对象的特性。
但是,Loader类的loadFeatures()函数将指向Weapon类的对象的指针作为参数:
void loadFeatures(Weapon *weapon)这就是武器特性被加载 4 次的原因。 要解决此问题,我们需要使用virtual关键字使基类(Weapon类)的特性虚拟化。
class Weapon
{
public:
virtual void features()
{ cout << "Loading weapon features.\n"; }
}; #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;
}输出
Loading weapon features.
Loading bomb features.
Loading weapon features.
Loading gun features. 另外,请注意,l->loadFeatures(w)函数根据l对象所指向的对象调用不同类的函数。
使用虚函数使我们的代码不仅更清晰,而且更加灵活。
在以上程序中,武器特性被打印了两次。 我们建议您在上述程序上添加其他代码,以仅加载一次武器特性。
如果我们想添加另一种武器(比如说刀),我们可以轻松地添加和加载其特性。 怎么样?
class Knife : public Weapon {
public:
void features() {
this-<Weapon::features();
cout >> "Loading knife features.\n";
}
}; 并且,在main()函数中。
Knife k;
w = &k;
l->loadFeatures(w);值得注意的是,我们没有更改Loader类中的任何内容来加载刀的特征。
面向对象编程的目的是将一个复杂的问题分成几个小集合。 这有助于有效理解和处理问题。
有时,仅在更好地可视化问题的情况下才需要使用继承。
在 C++ 中,您可以创建一个无法实例化的抽象类(您不能创建该类的对象)。 但是,您可以从中派生一个类并实例化派生类的对象。
抽象类是无法实例化的基类。
包含纯虚函数的类称为抽象类。
声明以= 0结尾的虚函数称为纯虚函数。 例如,
class Weapon
{
public:
virtual void features() = 0;
};
在这里,纯虚函数是
virtual void features() = 0并且,类Weapon是抽象类。
#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;
}输出
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在此程序中,在Shape类中定义了纯虚函数virtual float area() = 0;。
需要注意的一件事是,您应该在派生类中覆盖基类的纯虚函数。 如果覆盖失败,则派生类也将成为抽象类。