Java 基础 - 多态(动态绑定)的底层原理:虚函数表

发布时间 2023-11-02 17:47:43作者: frank_cui

 

为了实现Java方法调用的动态绑定,HotSpot使用了与C++虚函数类似的机制,同时为了避免每个对象都维护一个虚函数表,就是设计了Oop-Klass模型,用Klass类保存类的元数据和虚函数表。

多态的理解

从java语言层面看,多态是指在编译期间不确定实际类型,在运行期间才根据实际类型调用具体的方法,一般在接口的多实现和类的继承有具体体现。
从JVM内部角度看,java多态的实现是通过itable(interface method table:接口方法表), vtable(virtual method table:虚函数表)来实现方法的准确跳转。
Java中的普通方法(没有static和final修饰)是动态绑定的,在C++中动态绑定通过虚函数来实现,代价是每个C++对象都必须维护一张虚函数表。Java的特点就是一切皆是对象,如果每个对象都维护一张虚函数表,内存开销将会非常大。JVM对此做了优化,虚函数表不再由每个对象维护,改成由oop-klass模型的Klass类型维护,所有属于同一类型的所有对象共用一张虚函数表。
注意: C++是每个对象持有虚函数表,java是通过OOP对象指向Klass对象,Klass对象持有该类的虚函数表,所有该类的对象共用一张虚函数表。java的普通方法都会在JVM都会在内部表示为类似于C++的虚函数。

C++的虚函数和纯函数的区别

纯函数: 在基类中只进行声明,不进行具体实现,而在子类进行具体实现,类似于java接口中的方法。
注意: C++允许多重继承,是通过抽象类来实现java中接口作用的,抽象类中都是纯函数


虚函数: 在基类中,使用virtual关键字修饰的函数,且有具体的实现,其子类可以覆写该虚函数,也可以不覆写该函数。虚函数在C++中的主要作用:实现多态和多重继承。

 

虚函数表

每一个含有虚函数(无论虚函数是其本身的,还是继承而来的)的类都至少有一个与之对应的虚函数表,其存放着该类所有虚函数对应的函数指针。


虚函数表的构造过程

子类重基类中拷贝一份虚函数表,使子类的虚函数表指针指向新的虚函数表,如果子类中覆写了基类的虚函数,则将函数表中覆写函数的函数指针替换为子类覆写的函数指针,如果子类中有新增的虚函数,则在该子类的虚函数表中追加新增的虚函数指针。


虚函数的调用过程

C++编译时,只知道pb是B类型的指针,并不知道该指针具体指向哪个对象,但无论pb指向哪个对象,被调用函数在虚函数表中偏移量是固定的,运行时根据传入对象的具体类型,找到对应的虚函数表,再根据相同虚函数在基类和不同子类中的偏移量相同,找到对应的虚函数执行。
多重继承: C++允许多重继承,当一个类继承多个类,且多个基类都有虚函数时,子类对象中将包含多个虚函数表的指针。

————————————————
版权声明:本文为CSDN博主「user2025」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/user2025/article/details/113742085