# C++ 虚函数

> 原文： [https://www.programiz.com/cpp-programming/virtual-functions](https://www.programiz.com/cpp-programming/virtual-functions)

#### 在本文中，您将了解虚拟函数及其使用位置。 另外，您还将学习纯虚函数和抽象类。

虚函数是您希望在派生类中重新定义的基类中的成员函数。

在详细介绍之前，让我们先了解一下为什么首先需要虚拟函数。

* * *

## 一个例子

让我们假设，我们正在开发一个游戏（特别是武器）。

我们创建了`Weapon`类，并派生了两个类`Bomb`和`Gun`以加载相应武器的特性。

```cpp
#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;
}
```

**输出**

```cpp
Loading weapon features.
Loading bomb features.
Loading gun features.
```

我们定义了`Weapon`，`Bomb`和`Gun`类的三个指针对象`w`，`b`和`g`。 并且，我们使用以下命令调用了每个对象的`loadFeatures()`成员函数：

```cpp
w->loadFeatures();
b->loadFeatures();
g->loadFeatures();
```

完美的作品！

但是，我们的游戏项目开始变得越来越大。 并且，我们决定创建一个单独的`Loader`类来加载武器特性。

此类`Loader`类根据选择的武器来加载武器的其他函数。

```cpp
class Loader
{
   public:
     void loadFeatures(Weapon *weapon)
     {
        weapon->features();
     }     
}; 
```

`loadFeatures()`加载特定武器的特性。

* * *

### 让我们尝试实现我们的`Loader`类

```cpp
#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;
}
```

**输出**

```cpp
Loading weapon features.
Loading weapon features. 
Loading weapon features.
Loading weapon features. 
```

我们的实现似乎是正确的。 但是，武器特性被加载了 4 次。 为什么？

最初，武器对象`w`指向`Bomb`类的`b`对象。 并且，我们尝试使用指向`Loader`类的指针的`l`对象将其传递给`loadFeatures()`函数来加载`Bomb`对象的特性。

同样，我们尝试加载`Gun`对象的特性。

但是，`Loader`类的`loadFeatures()`函数将指向`Weapon`类的对象的指针作为参数：

```cpp
void loadFeatures(Weapon *weapon)
```

这就是武器特性被加载 4 次的原因。 要解决此问题，我们需要使用`virtual`关键字使基类（`Weapon`类）的特性虚拟化。

```cpp
class Weapon
{
    public:
      virtual void features()
         { cout << "Loading weapon features.\n"; }
}; 
```

* * *

## 示例：使用虚函数解决问题

```cpp
#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;
}
```

**输出**

```cpp
Loading weapon features.
Loading bomb features.
Loading weapon features. 
Loading gun features. 
```

另外，请注意，`l->loadFeatures(w)`函数根据`l`对象所指向的对象调用不同类的函数。

使用虚函数使我们的代码不仅更清晰，而且更加灵活。

在以上程序中，武器特性被打印了两次。 我们建议您在上述程序上添加其他代码，以仅加载一次武器特性。

如果我们想添加另一种武器（比如说刀），我们可以轻松地添加和加载其特性。 **怎么样**？

```cpp
class Knife : public Weapon {
   public:
   void features() {
      this-<Weapon::features();
      cout >> "Loading knife features.\n";
   }
}; 
```

并且，在`main()`函数中。

```cpp
Knife k; 
w = &k; 
l->loadFeatures(w);
```

值得注意的是，我们没有更改`Loader`类中的任何内容来加载刀的特征。

* * *

## C++ 抽象类和纯虚函数

面向对象编程的目的是将一个复杂的问题分成几个小集合。 这有助于有效理解和处理问题。

有时，仅在更好地可视化问题的情况下才需要使用继承。

在 C++ 中，您可以创建一个无法实例化的抽象类（您不能创建该类的对象）。 但是，您可以从中派生一个类并实例化派生类的对象。

抽象类是无法实例化的基类。

包含纯虚函数的类称为抽象类。

* * *

## 纯虚函数

声明以`= 0`结尾的虚函数称为纯虚函数。 例如，

```cpp
class Weapon
{
    public:
      virtual void features() = 0;
};

```

在这里，纯虚函数是

```cpp
virtual void features() = 0
```

并且，类`Weapon`是抽象类。

* * *

## 示例：抽象类和纯虚函数

```cpp
#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;
}
```

**输出**

```cpp
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;`。

需要注意的一件事是，您应该在派生类中覆盖基类的纯虚函数。 如果覆盖失败，则派生类也将成为抽象类。