C++中的多态实际上就是让父类指针拥有多种形态。
C++使用虚函数来实现多态。
虚函数的原理就是在对象创建的同时创建了一张虚表。并且把虚表的地址放到对象的最顶部。那么父类指针肯定就可以获取到这个虚表。如果调用虚函数,则会直接通过虚表调用。
假设我们有两个类,
1 | class people { |
people有两个virtual函数,boy类只overwrite了一个。
我们先看一下底层是怎样调用虚函数的:
我们可以看到虚函数的call用到了call [edx]
而不是直接跳到一个固定的地方。这里就是间接跳转。
edx
里面存放的就是虚表的地址值,[edx]
就是虚表里面第一个地址。所以b
这个对象调用了虚表里面的第一个函数。
同样的,如果我们调用p
对象的laugh()
函数,也是调用p
对象虚表里面的第一个位置。
所以有了第一个结论:
虚表中的函数地址个数与virtual函数的个数一样,并且与virtual函数的位置对应(比如我这个例子中laugh()对应虚表第一个函数地址, piss()对应虚表第二个位置)
那么我们继续深入,看一下b
和p
这两个对象的虚表有什么区别:
因为对象最顶上的第一个4bytes存放的就是虚表的地址,我是再Stack中创建的对象,直接在栈中查看,如下:
得到p
的虚表地址为00b59c5c
; b
的虚表地址为00b59d5c
。
p
的虚表如下:
p
的虚表为00b51505
,00b5151e
。
b
的虚表如下:
b
的虚表为: 00b5150f
, 00b5151e
。
我们可以看到p
和b
的虚表都有两个值,分别对应laugh()
和piss()
。因为boy
类重写了laugh()
函数,所以boy
类的虚表中第一个地址是自己重写的laugh(),而people类的第一个地址是people类中的laugh()地址。
因为boy
类没有重写piss()
。所以boy
类的第二个地址就是people
类的piss()
地址。
所以第二个结论就是:
如果子类没有overwrite父类的virtual函数,那么子类虚表中对应的地址与最近的父类的对应地址相同。
Conclusion:
virtual function的实现方法如下: