C++ Virtual Void vs no virtual

C++ Virtual Void vs no virtual

本问题已经有最佳答案,请猛点这里访问。

我对虚拟函数感到困惑。我被告知父类中的虚拟意味着我可以在子类中重写它。但是,如果在父类中省略了虚拟,我仍然可以重写它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;

class Enemy{
  public:
    //if I remove virtual, it still gets overriden in child class
  virtual void attack(){

    cout <<"Attack Enemy!" << endl;
  }
};


class Minion : public Enemy {
  public:
  void attack(){
    cout <<"Attack Minon!" << endl;
  }
};
int main() {
  Minion m;
  m.attack();
}


您可以重写所讨论的方法,并在理想情况下使用override关键字声明,以允许编译器在不重写任何内容时抱怨。

1
2
3
4
5
6
7
8
9
10
11
class Enemy {
  public:
    virtual ~Enemy() = default; /* Don't forget... or non-virtual protected. */

    virtual void attack() {}
};

class Minion : public Enemy {
  public:
    void attack() override {}
};

您的困惑可能源于这样一个事实:您同样可以很好地隐藏基类方法。

1
2
3
4
5
6
7
8
9
10
class Enemy {
  public:
    virtual ~Enemy() = default;
    void attack() {}
};

class Minion : public Enemy {
  public:
    void attack() {}
};

这不仅做了一些不同的事情,而且是一个非常糟糕的命名的例子,因为它让读者感到困惑:在类层次结构的上下文中,具有相同签名的相同成员函数名不可避免地与被重写的方法相关联。


如果函数是虚拟的,那么调用将在运行时通过vtable动态调度到派生类型提供的实现,而如果没有,编译器将在编译时查看对象并选择静态类型的类方法。如果您有指向基类的指针,这意味着使用了基本实现,如果函数不是虚拟的:

Enemy *e = new Minion(); e->attack();

将打印"攻击敌人!"如果attack不是虚拟的。当使用虚函数时,编译器会插入一些逻辑来在运行时查找正确的实现,当执行e->attack()时,在e指向的对象指针vtable中查找实现,发现Minion重写attack从而打印"攻击最小值!".

如果要强制派生类重写基类方法,还可以使用

virtual void attack() = 0;


您没有重写它,只是添加了另一个具有相同签名的方法。

1
2
3
4
5
Enemy* enemy = new Minion();

enemy->attack();

delete enemy;

如果这调用了Minion的代码,那么您做得对。如果它从Enemy调用代码,那么您做了一些错误。你需要virtual来做正确的事情。