为什么 C++ RTTI 要求类有一个虚拟方法表?虽然使用表作为多态向上转换的手段似乎是合理的,但从设计的角度来看,它似乎并不是严格要求的。例如,该类可以包含传递信息的散列或唯一标识符。
对于认为这个问题过于琐碎的 C++ 专家,这将有助于这个问题的发帖者(他是 C++ 的一个谦逊的初学者)从 RTTI 的设计角度解释为什么需要 vtables,以及什么是其他设计方法(而不是使用 vtables)来实现 RTTI(以及它们为什么工作/不工作以及 vtables)。
最佳答案
从语言的角度来看,答案是:它不是 . C++ 标准中没有任何地方说明如何实现虚函数。编译器可以自由地确保调用正确的函数,但它认为合适。
那么,通过替换 v 可以获得什么?指针 (不是 v 表 )具有 id 并删除 vtable? (用 id 替换 vtable 并没有任何帮助,一旦你解决了 vptr,你就已经知道运行时类型了)
运行时如何知道实际调用哪个函数?
考虑:
template <int I>
struct A {
virtual void foo() {}
virtual void bar() {}
virtual ~A() {}
};
template <int I>
struct B : A<I> {
virtual void foo() {}
};
假设你的编译器给出 A<0>
... 让我们称之为 视频 ... 0 和 A<1>
视频 1. 请注意 A<0>
和 A<1>
在这一点上是完全不相关的类。如果你说 a0.foo()
会发生什么?在哪里 a0
是 A<0>
?在运行时,非虚拟函数只会导致静态调度 call
.但是对于虚函数,调用函数的地址必须在运行时确定。如果你只有 vid 0,你仍然需要编码你想要的功能。这将导致 if-else 分支的森林,以找出正确的函数指针。
if (vid == 0) {
if (fid == 0) {
call A<0>::foo();
} else if (fid == 1) {
call A<0>::bar();
} /* ... */
} else if (vid == 1) {
if (fid == 0) {
call A<1>::foo();
} else if (fid == 1) {
call A<1>::bar();
} /* ... */
} /* ... */
这将失控。因此,表。添加标识 foo()
的偏移量以 A<0>
为基数的函数的 vtable 并且您有要调用的实际函数的地址。如果您有 B<0>
相反,将偏移量添加到该类的表的基指针。理论上,编译器可以为此发出 if-else 代码,但事实证明,指针添加速度更快,生成的代码更小。
关于c++ - 为什么 C++ RTTI 需要虚拟方法表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63025648/