为什么需要在虚拟表中输入纯虚函数

时间:2015-07-09 03:40:46

标签: c++

我对虚拟表的理解是,每当编译器在类中找到虚函数时,它就会为类创建一个虚拟表,虚函数的所有函数指针都将放在该表中。

但是当谈到纯虚函数时,我们不会在任何时候调用该函数。那么为什么在虚拟表中需要输入纯虚函数。

virtual void myFunction() = 0 ;

2 个答案:

答案 0 :(得分:5)

您无法实例化抽象类的对象。这实际上使你的问题变得毫无意义:因为你永远不会实例化你的抽象类,所以根本不需要该类的虚拟表。 (实际上,在施工/毁坏期间可能需要暂时使用,但这是另一回事。)

当您实际实例化一个对象时,它是某个派生类的对象,它不再是抽象的。它不再具有任何纯虚函数。实际实例化的派生类具有被该时间覆盖的所有纯虚函数。这就是虚方法表中需要一个条目 - 存储指向实际覆盖函数的指针的原因。

稍后在您的代码中,您可以通过指向该抽象基类

的指针调用myFunction()
MyAbstractBaseClass *ptr = some_function();
// Pointer actually points to some non-abstract derived object
ptr->myFunction();

编译器将生成将进入与*ptr对象关联的虚拟方法表的代码,提取与myFunction()对应的指针条目,并通过该指针传递控制。如上所述,该指针实际上将指向某些派生类的重写函数。这正是该条目的保留。

答案 1 :(得分:4)

声明是必需的,因为你需要告诉编译器在vtable中保留一个特定方法的槽,从声明它的基类开始(这是你在调用方法时可能想要使用的类型)派生类)

只是为了给你一个想法让我们做一个例子(这不应该被认为是引擎盖下发生的事情)。假设您在Base中有三个虚拟方法,其中一个是纯粹的,

class Base {
  virtual void pure() = 0;
  virtual void nonpure() { }
  virtual void nonpure2() { }
};

所以Base vtable看起来像

0 [ pure ] -> nothing
1 [ nonpure ] -> address of Base::nonpure
2 [ nonpure2] -> address of Base::nonpure2

现在让我们用

来推导它
class Derive : public Base {
  virtual pure() override { }
  virtual nonpure2() override { }
};

Derived vtable看起来像

0 [ pure ] -> address of Derived::pure
1 [ nonpure ] -> address of Base::nonpure
2 [ nonpure2 ] -> address of Derived::nonpure2

当你尝试做

Base* derived = new Derived();
derived->pure();

该方法大致编译为

address = derived->vtable[0];
call address

如果你没有在Base类中声明纯虚方法,在这种情况下,在编译时无法知道它在vtable(0)中的索引,因为该方法根本不存在。

但是作为vtable中的一个洞,你无法实例化一个带有空“槽”的vtable的类。