虚方法表

31

在讨论密封类时,经常提到“虚函数表”这个术语。它是什么?我之前读过一个方法表(我也不记得它的目的是什么了),在这里进行谷歌/搜索会出现与C++相关的结果。

谢谢。

3个回答

38

"虚函数表"或者 "虚方法表" 是每个类拥有的方法指针列表。它包含了类中虚方法的指针。

每个类的实例都有一个指向该表的指针,在从实例调用虚方法时使用。这是因为调用虚方法应该调用与实际对象的类相关联的方法,而不是与对象引用的类相关联的方法。

例如,如果你有一个指向字符串的对象引用:

object obj = "asdf";

并调用虚方法 ToString:

string text = obj.ToString();
它将使用String.ToString方法,而不是Object.ToString方法。它使用的是String类的虚拟方法表(指向字符串实例的指针),而不是Object类的虚拟方法表。

1
我是否正确理解“方法表”是针对任何类型创建的。因此,值类型也有一个“方法表”。还有一个问题,类型如何被认定为具有“方法表”?“方法表”存储或引用在类型对象中吗?但是值类型没有类型对象指针。:/ 这里有一个相关的问题:https://dev59.com/7JPfa4cB1Zd3GeqPG7Zk#35185851 - Anton Lyhin
2
@Spirit:是的,每种类型都有一个方法表(至少在某些形式和阶段上)。由于值类型的调用是非虚拟的,因此确切的方法在编译时确定,因此它们不需要虚拟方法表,对于它们而言,只需要为反射目的在可执行文件中存在即可。我不知道方法表实际存储在哪里,这取决于具体实现,因此您可以在框架源代码中查找(但是该实现的部分可能不是公开的)。 - Guffa
谢谢。你的回答非常清晰。我终于找到了一个类似的SO讨论:https://dev59.com/kHNA5IYBdhLWcg3whuV_ - Anton Lyhin
string text = obj.ToString(); 的 IL 代码是:callvirt instance string [mscorlib]System.Object::ToString(),因此它似乎实际上并没有使用 String.ToString - OfirD
2
@HeyJude obj的编译时类型是object。由于ToString是虚拟的且object是引用类型,因此调用将使用callvirt关键字发生。Callvirt将在查看obj实例的虚拟方法表后调用String.ToString()。它可能看起来不会调用String.ToString,但实际上确实会这样做。 - Noel Widmer
非虚拟方法是否包含在虚拟表中,还是它们有一个单独的非虚拟方法表?@Guffa - Claude Tan

10
C#虚函数表的工作原理基本与C++相同,因此任何描述C++虚函数表如何工作的资源都应该对C#有很大帮助。
例如,维基百科的描述不错。

1
通常情况下,答案应该是自包含的,不应链接到任何第三方文本,尤其不应链接到维基百科的文章。 - Marine Galantin

2

虚函数表(常称为vtable)的定义在C#和C++中是一致的。它是一个由运行时内部使用的查找表,用于实现抽象/虚拟/覆盖方法的多态行为。

简单地说,你可以想象一个具有虚方法A的基类将拥有一个包含方法A的vtable。当派生类重写方法A时,方法A的vtable继承自父类,但现在会指向派生类中的方法A而不是父类中的方法A。


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