首先,我想明确的是我确实明白 C++ 标准中没有 vtable 和 vptrs 的概念。然而,我认为几乎所有实现都以几乎相同的方式实现虚拟调度机制(如果我错了,请纠正我,但这不是主要问题)。另外,我相信我知道虚拟函数是如何工作的,也就是说,我总是可以知道将调用哪个函数,我只需要实现细节。
假设有人问我以下问题:
“您有带有虚拟函数 v1、v2、v3 的基类 B 和派生类 D:B,它重写函数 v1 和 v3 并添加虚拟函数 v4。请解释虚拟调度的工作原理”。
我会这样回答:
对于每个具有虚函数的类(在本例中为 B 和 D),我们有一个单独的函数指针数组,称为 vtable。
B 的 vtable 将包含
&B::v1
&B::v2
&B::v3
D 的 vtable 将包含
&D::v1
&B::v2
&D::v3
&D::v4
现在B类包含一个成员指针vptr。 D 自然地继承了它,因此也包含了它。在 B 的构造函数和析构函数中,B 设置 vptr 指向 B 的 vtable。在D的构造函数和析构函数中,D将其设置为指向D的vtable。
对多态类 X 的对象 x 上的虚函数 f 的任何调用都被解释为对 x.vptr[f 在 vtable 中的位置] 的调用
问题是:
1.以上描述是否有错误?
2.编译器如何知道f在vtable中的位置(请详细说明)
3. 这是否意味着如果一个类有两个基数,那么它就有两个 vptr?在这种情况下发生了什么? (尝试以与我类似的方式描述,尽可能详细)
4. A 在顶部 B、C 在中间、D 在底部的钻石层次结构中会发生什么? (A是B和C的虚拟基类)
提前致谢。
1.以上描述是否有错误?
都好。 :-)
2.编译器如何知道f在vtable中的位置
每个供应商都有自己的方法来做到这一点,但我总是将 vtable 视为成员函数签名到内存偏移量的映射。所以编译器只维护这个列表。
3. 这是否意味着如果一个类有两个基数,那么它就有两个 vptr?在这种情况下发生了什么?
通常,编译器会组成一个newvtable,由按指定顺序附加在一起的所有虚拟基址的 vtable 以及虚拟基址的 vtable 指针组成。他们遵循派生类的 vtable 函数。这是极其供应商特定的,但对于class D : B1, B2
,你通常会看到D._vptr[0] == B1._vptr
.
该图像实际上用于组成对象的成员字段,但编译器可以以完全相同的方式组成虚函数表(据我所知)。
4. A 在顶部 B、C 在中间、D 在底部的钻石层次结构中会发生什么? (A是B和C的虚拟基类)
简短的回答?绝对的地狱。您实际上继承了这两个基础吗?只是其中之一吗?他们都不是?最终,使用了为类编写 vtable 的相同技术,但是如何完成此操作的方式差异很大,因为how应该这样做并不是一成不变的。解决钻石层次结构问题有一个不错的解释here http://www.phpcompiler.org/articles/virtualinheritance.html,但是,像大多数这样,它是特定于供应商的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)