C++编译器在编译阶段对指针(引用)调用虚函数的处理

 

 


关于c++虚函数多态的内部机制,有些经典的书籍已经给出了详细的阐述,例如侯捷先生的《深度探索C++对象模型》。

在此做些小总结。

C++编译器在编译阶段对指针(引用)调用虚函数的处理

>类的存储空间:
 1) 含有虚函数的类有一个虚函数表,存储各个虚函数的地址。
 2) 每个对象有一个虚函数指针,指向该其所属类的虚函数表;

编译期间, 编译器会为每一个含有虚函数的类生成一个虚函数表, 这个虚函数表的大小必须在编译阶段确定下来,这也导致了模板函数不能为虚函数(https://blog.****.net/qq_35865125/article/details/109234801)。

编译期间的另一项重要工作是,编译器会替换所有通过指针(引用)访问虚函数的地方,替换成 虚表[idx]。例如,p->func(), func函数在虚表中的位置是已经确定了的。 嗯,也就是说,基类的func函数在其虚函数表中的位置索引 = 各个子类的func函数在其虚函数表中的位置索引 , 这样才能保证通过一个固定的编译期间写死的索引值来定位到func函数。
 
>执行:
Base* p = new subClass;

p->func();

在编译期间,p->func()已经被替换为p->vptr[3], (假设func函数在虚表中的索引为3)。程序执行到这里时,会去p指向的地址空间取出虚函数表指针,另外,这个时候p指向的对象是哪个子类已经确定了,只需要到这个地址取它的虚指针就没错了,如果p指向的subClass A的对象地址,则,取出的是subClass A的虚指针。(虽然是父类的指针,但是它指向的还是子类的空间呀呀).。然后,就会顺利成章地调用到正确的虚函数了。

 

虚表实现多态是支持面向对象语言的最大特点吧(另外,C++的模板技术可实现静态多态)。 在c++以后,c#,Java都相继扩展了虚表的使用范围,这些语言背后的设计这都是高手,他们在权衡了多态的利与弊之后还是做出了扩展虚表的决定。


C++编译器在编译阶段对指针(引用)调用虚函数的处理

yes, 这种非多态的函数调用,编译阶段即可定位出要调用的是哪个具体的函数,跟普通的c语言函数调用一样哦。

 


Ref:

《深度探索C++对象模型》

https://www.cnblogs.com/ViCross/archive/2008/09/28/1301767.html