C++内存分布
虚函数调用的原理 我们知道,在C++中使用指向对象的指针或引用才能触发虚函数的调用,产生多态的结果。例如对于如下的代码片断: Base *p; ...... p->vfunc(); // vfunc是Base中声明的virtual函数
由于指针p可以指向一个Base的对象,也可以指向Base的派生类的对象,而编译器在编译时并不知道p所指向的真实对象到底是什么,那么究竟如何判断呢?
从各种的C++对象的内存分布中可以看到,尽管虚函数表中的虚函数地址可能被更新(派生类重写基类的virtual函数)或添加新的项(派生类声明新的virtual函数),但是一个相同签名的虚函数在虚函数表中的索引值却是不变的。所以无论p指向的是Base的对象,还是Base的派生类的对象,其virtual函数vfunc在虚函数表中的索引是不变的(均为1)。
在了解了C++对象的内存布局后,就能轻松地回答这个问题了。因为在编译时,编译器根本无需判断p所指向的具体对象是什么,而是根据指针p所指向的对象的Base子对象中的虚函数表来实现函数调用的。编译器可能会把virtual函数调用的代码修改为如下的伪代码: (*p->vptr[1])(p); // 假设vfunc函数在虚函数表中的索引值为1,参数p为this指针
若p指向的是一个Base的对象,则调Base的虚函数表中索引值为1的函数。若p指向的是一个Base的派生类的对象,则调用Base的派生类对象的Base子对象的虚函数表中的索引值为1的函数。这样便实现了多态 。这种函数调用是根据指针p所指的对象的虚函数表来实现的,在编译时由于无法确定指针p所指的真实对象,所以无法确定真实要调用哪一个函数,只有在运行时根据指针p所指的对象来动态决定。所以说,虚函数是在运行时动态绑定的,而不是在编译时静态绑定的。