一、概述
二、类的虚表
class A {public:virtual void vfunc1();virtual void vfunc2();void func1();void func2();private:int m_data1, m_data2;};

虚表内的条目,即虚函数指针的赋值发生在编译器的编译阶段,也就是说在代码的编译阶段,虚表就可以构造出来了。
三、虚表指针
*__vptr,用来指向虚表。这样,当类的对象在创建时便拥有了这个指针,且这个指针的值会自动被设置为指向类的虚表。

四、动态绑定
class A {public:virtual void vfunc1();virtual void vfunc2();void func1();void func2();private:int m_data1, m_data2;};class B : public A {public:virtual void vfunc1();void func1();private:int m_data3;};class C: public B {public:virtual void vfunc2();void func2();private:int m_data1, m_data4;};

*__vptr,用来指向自己所属类的虚表。
A::vfunc1()和
A::vfunc2()。
B::vfunc1()函数,故B vtbl的两个指针分别指向
B::vfunc1()和
A::vfunc2()。
C::vfunc2()函数,故C vtbl的两个指针分别指向
B::vfunc1()(指向继承的最近的一个类的函数)和
C::vfunc2()。
bObject。由于
bObject是类B的一个对象,故
bObject包含一个虚表指针,指向类B的虚表。
int main(){B bObject;}
bObject。虽然
p是基类的指针只能指向基类的部分,但是虚表指针亦属于基类部分,所以
p可以访问到对象
bObject的虚表指针。
bObject的虚表指针指向类B的虚表,所以
p可以访问到B vtbl。如图3所示。
int main(){B bObject;A *p = & bObject;}
p来调用
vfunc1()函数时,会发生什么现象?
int main(){B bObject;A *p = & bObject;p->vfunc1();}
p->vfunc1()时,会发现
p是个指针,且调用的函数是虚函数,接下来便会进行以下的步骤。
p->__vptr来访问对象
bObject对应的虚表。虽然指针
p是基类
A*类型,但是
*__vptr也是基类的一部分,所以可以通过
p->__vptr可以访问到对象对应的虚表。
p->vfunc1()的调用,B vtbl的第一项即是
vfunc1对应的条目。
B::vfunc1(),所以
p->vfunc1()实质会调用
B::vfunc1()函数。
p指向类A的对象,情况又是怎么样?
int main(){A aObject;A *p = &aObject;p->vfunc1();}
aObject在创建时,它的虚表指针
__vptr已设置为指向A vtbl,这样
p->__vptr就指向A vtbl。
vfunc1在A vtbl对应在条目指向了
A::vfunc1()函数,所以
p->vfunc1()实质会调用
A::vfunc1()函数。
(*(p->__vptr)[n])(p)
通过指针来调用函数 指针upcast向上转型(继承类向基类的转换称为upcast,关于什么是upcast,可以参考本文的参考资料) 调用的是虚函数
五、总结
参考资料
《C++ Primer》第三版,中文版,潘爱民等译 http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/ 侯捷《C++最佳编程实践》视频,极客班,2015 Upcasting and Downcasting, http://www.bogotobogo.com/cplusplus/upcasting_downcasting.php
文章转载自CPP开发前沿,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




