C ++继承/虚函数表问题

10

更新:用直接的方法调用示例替换了析构函数示例。

您好,

如果我有以下代码:

class a
{
public:
    virtual void func0(); // a has a VTable now
    void func1();
};
class b : public a
{
public:
    void func0() { a::func0(); }
    void func2();
};
  1. B中有VTable吗?B没有虚函数,但从b::func0()调用a::func0()。
  2. func1是否在VTable中?它不是虚函数。
  3. func2是否在VTable中?
  4. 如果b::func0()中没有a::func0()的调用,上述答案会有所不同吗?

谢谢。


5
请告诉我们您使用的编译器,这样会更好。虚拟表是实现特定的,尽管有努力标准化它们的格式。 - P Shved
4个回答

19
如果你声明了虚函数,你也应该将析构函数声明为虚函数 ;-).
  1. 类B有一个虚表,因为它有一个虚函数,即func0()。如果在基类中声明一个函数(包括析构函数)为虚函数,所有派生类将具有相同签名的虚函数,并且会导致它们拥有虚表。此外,即使您没有在类B中明确声明func0,它也会有虚表。

  2. 非虚函数不通过虚表引用。

  3. 参见2。

  4. 不是。类的虚表基于类声明构建。不考虑类函数(更不用说其他函数)的实现。因此,B有一个虚表,因为其函数func0()是虚函数。

还有一个棘手的细节,尽管它不是您问题的要点。您将函数B::func0()声明为内联函数。在gcc编译器中,如果一个虚函数被声明为内联函数,则它将保留其在虚表中的槽位,该槽位指向为该内联函数发出的特殊函数(这算作取地址,从而使内联函数发出)。这意味着,无论函数是否为内联函数,它都不会影响虚表中的槽位数量和类所需的虚表。


更新了。因此,如果在继承结构中的任何基类中声明了一个虚函数,则该函数即使直接基类(1 级别以上)未将其标记为虚函数,也会成为虚函数? - jameszhao00
@jameszhao00:还更新了答案。你说得对:如果任何间接的基类(即当前类的某个父类的某个父类...)声明了虚函数,那么在当前类中它也是虚函数。请注意,重载函数(void f(int)与void f(double)进行比较)被视为不同的函数。 - P Shved

5
  1. 是的,因为它的基类有一个; 而且即使您没有声明它,它的析构函数也是虚拟的,因为基类的析构函数是虚拟的。

  2. 不是。

  3. 不。

  4. 不是。实际上,我认为当前的代码不合法:即使您不从~B显式调用~A,编译器也会在调用B析构函数之后调用A析构函数;因此,我不认为您应该从~B调用~A,即使编译器允许您这样做。


抱歉,我不应该以析构函数作为例子。已更新。 - jameszhao00

3

参考更新后的示例:

  1. 是的,b 有一个虚函数表。请注意,b::func0() 是虚拟的(覆盖了 a::func0()),即使您没有明确将其标记为虚拟的。我想这是C++中的一个奇怪漏洞。
  2. 不是的。非虚函数不在虚函数表中。
  3. 参见2。
  4. 不是的。您已经重写了a:func0();无论是否调用a::func0()都没有关系。

一些额外的注释(依赖于编译器,但这些是相当常见的概括):

  • 每个 b 的实例都会有一个指向虚函数表的指针,因为您从具有虚函数的类派生而来。
  • 即使您没有定义 b::func0(),也会出现这种情况。
  • 在这种情况下,编译器可能会让 b 的实例指向 a 的静态虚函数表,或者它可能会创建一个静态虚函数表,然后填充指向 a 成员的指针的指针。
  • 但是仍然需要这样做,以便您可以通过指向 a 的指针正确访问 b 的实例。

1
  • 如果基类函数是虚函数,那么如果你在派生类中重写该函数,即使你没有明确指定,它也会隐式地成为虚函数。如果类有一个虚函数,则它有一个虚表。
  • 只有虚函数存在于虚表中,function1不会驻留在虚表中
  • function2也不会因为上述原因而驻留在虚表中
  • 虚表的创建不取决于您是否从基类或其他地方调用该函数。函数调用不决定虚表的创建。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接