好吧,这是一个有趣的问题,但让我尝试以非常有条理的方式回答这个问题!
假设编译器必须解决这样的调用:*
a->someFunc();
*。
现在,编译器将有条不紊地执行以下步骤。
1.) 首先,编译器知道变量 a 的声明类型,所以它会检查对象 a ( lets call this, class A for time being
) 的声明类型是否有一个名为 someFunc() 的方法,并且它必须是公共的。此方法可以在 中声明 class A
,也可以是 A 类的基类之一的派生方法,但这对编译器无关紧要,它只是使用访问说明符来检查它是否存在public
。
2.) 其次,一旦方法被验证为 A 类的一部分,编译器必须解析对正确方法的调用,因为许多方法可能同名(由于函数重载)。这个解决正确方法的过程称为overloading resolution
。编译器通过将被调用方法的签名与作为类的一部分的所有重载方法进行匹配来实现这一点。因此,someFunc() s
将找到并进一步考虑所有唯一正确的 someFunc() (将签名与调用的方法匹配)。
3.) 现在是困难的部分,很可能 someFunc() 可能已在类 A ( lets call this class AA and needless to say it is some subclass of A
) 的子类之一中被覆盖,并且变量 a (声明为 A 类型)实际上可能是指到 AA 类的对象,(这在 C++ 中允许启用多态性)。现在,如果 someFunc() 方法在基类(即 A 类)中声明为 type virtual
,并且 someFunc() 已被 A 的子类(在 AA 中或 A 和 AA 之间的类)覆盖,则编译器必须找出正确版本的 someFunc()。
现在,假设您是编译器,并且您的任务是查找该类是否AA
具有此方法。显然,AA 类将具有此方法,因为它是 A 的子类,并且 A 类中 A 的公共访问已在步骤 1 中由编译器验证!. 但或者,如前一段所述, someFunc() 可能被 AA 类(或 A 和 AA 之间的任何其他类)覆盖,这是编译器需要捕获的。因此,您(因为您正在玩编译器)可以进行系统检查以找到最底层(继承树中最低)的重写方法 someFunc() 从类 A 开始并在类 AA 结束。在此搜索中,您将寻找与在重载解决方案中验证过的相同的方法签名。该方法将是将被调用的方法。
现在,您可能想知道,“这到底是怎么回事”,每次搜索都完成了吗?......嗯,不是真的。编译器知道每次找到这个的开销,因此,维护一个Virtual Table
为每个类类型调用的数据结构。将虚拟表视为从方法签名(可公开访问)到函数指针的映射。该虚拟表由编译器在编译过程中生成,并在程序执行期间保存在内存中。在我们的示例中,A 类和 AA 类都有自己的虚拟表。而当编译器必须在AA类中查找someFunc()时(因为变量a指向的实际对象是AA类型),它会简单地通过AA类的虚表找到函数指针。这就像散列到表中一样简单,并且是一个恒定时间操作。
问候
AVID