在本文中,您将了解虚函数及其使用位置。此外,您还将学习纯虚函数和抽象类。
虚函数是基类中的成员函数,您希望在派生类中重新定义这些函数。
在详细介绍之前,让我们先了解一下为什么首先需要虚函数。
让我们假设,我们正在开发一个游戏(比如道具:武器)。
我们创建了Weapon该类并派生了两个类,Bomb和Gun加载了各自武器的功能。
#include <iostream> using namespace std; class Weapon { public: void loadFeatures() { cout << "载入武器特性。\n"; } }; class Bomb : public Weapon { public: void loadFeatures() { cout << "装载刀的特性。\n"; } }; class Gun : public Weapon { public: void loadFeatures() { cout << "装载枪的特性\n"; } }; int main() { Weapon *w = new Weapon; Bomb *b = new Bomb; Gun *g = new Gun; w->loadFeatures(); b->loadFeatures(); g->loadFeatures(); return 0; }
输出结果
装载武器特性。 装载刀的特性。 装载枪的特性。
我们分别定义了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 << "装载武器特性。\n"; } void features() { cout << "装载武器特性。\n"; } }; class Bomb : public Weapon { public: void features() { this->Weapon::features(); cout << "装载刀的特性。\n"; } }; class Gun : public Weapon { public: void features() { this->Weapon::features(); cout << "加载枪的特性。\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; }
输出结果
装载武器特性。 装载武器特性。 装载武器特性。 装载武器特性。
我们的实现似乎是正确的。但是,装载武器特性被加载了4次。为什么?
最初,武器对象w指向(Bomb)类的b对象。 并且,我们尝试使用l对象指向(Loader类的)指针将其传递给loadFeatures()函数来加载Bomb对象的特性。
同样,我们尝试加载Gun对象的特性。
但是,Loader类的loadFeatures()函数将指向Weapon类对象的指针作为参数:
void loadFeatures(Weapon *weapon)
这就是为什么武器特性被加载4次的原因。为了解决这个问题,我们需要使用virtual关键字实现基类(Weapon类)的虚函数。
class Weapon { public: virtual void features() { cout << "装载武器特性。\n"; } };
#include <iostream> using namespace std; class Weapon { public: virtual void features() { cout << "装载武器特性。\n"; } }; class Bomb : public Weapon { public: void features() { this->Weapon::features(); cout << "装载刀的特性。\n"; } }; class Gun : public Weapon { public: void features() { this->Weapon::features(); cout << "加载枪的特性。\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; }
输出结果
装载武器特性。 装载刀的特性。 装载武器特性。 加载枪的特性。
另外,注意,l->loadFeatures(w)函数会根据l对象所指向的对象调用不同类的函数。
使用虚函数使我们的代码不仅更加清晰,而且更加灵活。
在以上程序中,“装载武器特性。”被打印了两次。我们建议您在上述程序上添加其他代码,以便只加载一次武器特性。
如果我们想添加另一种武器(比如说 弓),我们可以轻松地添加和加载其特性。如何添加?
class Bow : public Weapon { public: void features() { this-<Weapon::features(); cout >> "加载弓的特性。\n"; } };
并且,在main()函数中添加如下代码。
Bow b; w = &b; l->loadFeatures(w);
值得注意的是,我们没有更改Loader类中的任何内容来加载刀的特性。
面向对象编程的目的是将一个复杂的问题分成几个小集合。这有助于有效理解和处理问题。
有时,最好仅在更好地可视化问题的情况下使用继承。
在C ++中,您可以创建一个无法实例化的抽象类(您不能创建该类的对象)。但是,您可以从中派生一个类并实例化派生类的对象。
抽象类是无法实例化的基类。
包含纯虚函数的类称为抽象类。
声明以结尾的=0虚函数称为纯虚函数。例如,
class Weapon { public: virtual void features() = 0; };
在这里,纯虚函数是
virtual void features() = 0
并且,该类Weapon是抽象类。
#include <iostream> using namespace std; // 抽象类(不允许实例化的类) class Shape { protected: float l; public: void getData() { cin >> l; } // 虚函数 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 << "输入长度来计算正方形的面积: "; s.getData(); cout<<"正方形的面积: " << s.calculateArea(); cout<<"\n输入半径以计算圆的面积: "; c.getData(); cout << "圆的面积: " << c.calculateArea(); return 0; }
输出结果
输入长度来计算正方形的面积: 4 正方形的面积: 16 输入半径以计算圆的面积: 5 圆的面积: 78.5
在此程序中,纯虚函数virtual float area()= 0; 在Shape类中定义。
需要注意的一件事是,您应该在派生类中重写基类的纯虚函数。 如果重写失败,则派生类也将成为抽象类。