完全纯虚拟类的Vtable放置

5
根据我(有限的)对C++规范的了解,具有虚成员的类的vtable被放置在第一个非纯虚的非内联虚方法的定义处。那么编译器如何处理从所有纯虚方法(例如接口)继承的类?在这种情况下,vtable放置在哪里?

3
C++规范本身并没有提及虚函数表。 - John Dibling
1
一个繁琐的实现细节。MSVC++有__declspec(novtable)来抑制这样的虚函数表。还有非标准的__interface关键字来做同样的事情。 - Hans Passant
3个回答

6
vtable 存储已实现虚方法的地址。如果一个类的所有方法都是纯虚函数且未实现,则不需要生成 vtable。
如果没有从中派生并实现该方法的类,那么您将无法使用这样的类。每个实现虚拟方法的类都有自己的单个 vtable,其中包含所有虚拟方法的地址:它不以任何方式引用基类的 vtable; 地址是重复的。因此,如果您有一个继承自另一个类的类,那么该类将仅使用自己的 vtable。它不关心基类的 vtable; 甚至不需要存在此 vtable。
C++ 规范本身并不涉及 vtable; 它们只是一种常见的编译器行为。
2020 年的编辑:我大约十年前就写了这篇文章。我怀疑我是根据记忆和个人经验写的。下面的两条评论表明编译器会为基类创建 vtable(以便抛出错误),尽管如果是这样的话,我不知道如何构造对象,并且某些编译器会重用基类的 vtable。自 2011 年以来,似乎没有添加任何内容,而我现在正在认知下降,很难再深入思考,因此,如果这里的某个细节很重要,请自行进行研究。

有趣。我本以为拥有重复的表格会违反某种定义规则。 - tgoodhart
没有任何表是重复的。只有在类实现了虚方法时才会生成新的表;这将创建一个包含指向该实现的指针的唯一vtable。对于任何未更改的虚方法,表将包含重复的地址。 - fuzzyTew
1
如果一个类的所有方法都是纯虚函数,那么就没有实现。但这并不完全正确。在C++中,纯虚函数并不意味着没有实现。 - Ben Voigt
实际上,即使没有用户定义的实现,编译器也会为纯虚函数生成带有错误存根的条目。这样做是为了在调用纯虚函数时捕获和报告错误。 - Gene Bushuyev
每个实现虚方法的类都有自己的单个vtable,其中包含所有虚方法的地址:它不以任何方式引用基类的vtable。但这并不一定是真的。一些旧的Borland编译器实现了指向基础vmt的指针方案以节省内存。似乎没有人再关心这个了。 - Gene Bushuyev
显示剩余2条评论

3
C++标准没有指定任何有关vtable位置的规定,甚至没有规定v-table的存在。它只规定了行为,而v-table恰好是最直接的实现方式,因此被广泛使用。
从实际角度来看,抽象类存在v-table的唯一原因是在构造和析构期间使用,当对象的动态类型是抽象类时。
在仅具有纯虚函数的类中,显然不能有构造函数(因为构造函数不能是虚拟的)。但是,析构函数确实可以是虚拟的。
您的类仍然可以具有带有实现的pure virtual destructor,然后需要v-table(或等效的实现细节)。
但是,纯虚函数的实现很少见,并且不会在定义接口时执行。

2

只有实例化后才需要虚函数表。


什么实例?编译器在编译单个TU时不知道纯虚函数是否在另一个TU中有定义。编译器实现者可以决定如何处理它,创建vtable并将指针设置为错误函数以报告对未定义的纯虚函数的非法调用可能是一个合理的解决方案。 - Gene Bushuyev
自从我写下这段话(通过编辑通知),已经过了一段时间,但我想我的观点是:要么它是抽象的(具有纯虚方法),因此无法实例化并且不需要vtable,要么它不是,因此具有所有继承的纯虚方法被覆盖。OP的情况是全纯抽象的。 编译器在编译任何TU时都应该知道方法是否是纯的,因为它包含在类声明中。 - JB.

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