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

Nih*_*har 0 c++

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

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

virtual void myFunction() = 0 ;
Run Code Online (Sandbox Code Playgroud)

Jac*_*ack 6

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

只是为了给你一个想法,让我们举一个例子(这不是真正考虑在引擎盖下发生的事情)。假设您在 中有三个虚拟方法Base,其中一个是纯方法,

class Base {
  virtual void pure() = 0;
  virtual void nonpure() { }
  virtual void nonpure2() { }
};
Run Code Online (Sandbox Code Playgroud)

所以Basevtable 看起来像

0 [ pure ] -> nothing
1 [ nonpure ] -> address of Base::nonpure
2 [ nonpure2] -> address of Base::nonpure2
Run Code Online (Sandbox Code Playgroud)

现在让我们推导出它

class Derive : public Base {
  virtual pure() override { }
  virtual nonpure2() override { }
};
Run Code Online (Sandbox Code Playgroud)

Derived vtable 看起来像

0 [ pure ] -> address of Derived::pure
1 [ nonpure ] -> address of Base::nonpure
2 [ nonpure2 ] -> address of Derived::nonpure2
Run Code Online (Sandbox Code Playgroud)

当你然后尝试做

Base* derived = new Derived();
derived->pure();
Run Code Online (Sandbox Code Playgroud)

该方法大致编译为

address = derived->vtable[0];
call address
Run Code Online (Sandbox Code Playgroud)

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

但是作为 vtable 中的一个洞,你不能实例化一个具有空“槽”的 vtable 的类。


AnT*_*AnT 5

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

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

稍后在代码中,您可以myFunction()通过指向该抽象基类的指针进行调用

MyAbstractBaseClass *ptr = some_function();
// Pointer actually points to some non-abstract derived object
ptr->myFunction();
Run Code Online (Sandbox Code Playgroud)

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