Loading... 参考博客:https://blog.csdn.net/gaoxin1076/article/details/8298279 建议直接观看大佬博客: 本篇文章仅作为博主笔记使用 ![image.png](http://www.irohane.top/usr/uploads/2021/03/3209446113.png) 虚函数是动态联编的基础;虚函数是成员函数,而且是非静态的成员函数;虚函数在派生类中可能有不同的实现,当使用这个成员函数操作指针或引用所标识的对象时,对该成员函数的调用采用动态联编方式,即:在程序运行时进行关联或束定调用关系; 动态联编只能通过指针或引用标识对象来操作虚函数;如果采用一般的标识对象来操作虚函数,将采用静态联编的方式调用虚函数; 如果一个类具有虚函数,那么编译器就会为这个类的对象定义一个指针成员,并让这个指针成员指向一个表格,这个表格里面存放的是类的虚函数的入口地址;比如:一个基类里面有一些虚函数,那么这个基类就拥有这样一个表,它里面存放了自己的虚函数的入口地址,其派生类继承了这个虚函数表,如果在派生类中重写/覆盖/修改了基类中的虚函数,那么编译器就会把虚函数表中的函数入口地址修改成派生类中的对应虚函数的入口地址;这就为类的多态性的实现提供了基础; 虚函数按照其声明顺序存放于虚函数表中; 父类的虚函数存放在子类虚函数的前面; 多继承中,每个父类都有自己的虚函数表; 子类的成员函数被存放于第一个父类的虚函数表中; ## 代码 ``` #include <iostream> #include <Windows.h> //函数指针 typedef void(*Fun)(void); using namespace std; class Base { public: virtual void func1() { printf("Func1"); }; virtual void func2() { printf("Func2"); }; virtual void func3() { printf("Func3"); }; }; Fun fun; void main() { Base Cbase; printf("虚函数表地址:0x%08X\n", *(int *)(&Cbase)); printf("第一个函数地址:0x%08X\n", (int *)*(int*)(&Cbase)); fun = (Fun)*((int*)*(int*)(&Cbase)); fun(); return; } ``` ![image.png](http://www.irohane.top/usr/uploads/2021/03/2306339577.png) ![image.png](http://www.irohane.top/usr/uploads/2021/03/2603396207.png) ![image.png](http://www.irohane.top/usr/uploads/2021/03/1289385368.png) ![image.png](http://www.irohane.top/usr/uploads/2021/03/1329726513.png) ![image.png](http://www.irohane.top/usr/uploads/2021/03/130091403.png) ## 一般继承-无虚函数覆盖 父类有虚函数,但是子类并不覆盖父类,子类也有自己的虚函数在虚函数表中排列如下 ![image.png](http://www.irohane.top/usr/uploads/2021/03/266723491.png) ### 代码 ``` #include <iostream> #include <Windows.h> //函数指针 typedef void(*Fun)(void); using namespace std; class Base { public: virtual void func1() { printf("Func1\n"); }; virtual void func2() { printf("Func2\n"); }; virtual void func3() { printf("Func3\n"); }; }; Fun fun; class Base1 :public Base { public: virtual void func4() { printf("Func4\n"); }; virtual void func5() { printf("Func5\n"); }; virtual void func6() { printf("Func6\n"); }; }; void main() { Base1 Cbase; printf("虚函数表地址:0x%08X\n", *(int *)(&Cbase)); printf("第一个函数地址:0x%08X\n", (int *)*(int*)(&Cbase)); fun = (Fun) * ((int*)*(int*)(&Cbase)); fun(); fun = (Fun) * ((int*)*(int*)(&Cbase) + 1); fun(); fun = (Fun) * ((int*)*(int*)(&Cbase) + 2); fun(); fun = (Fun) * ((int*)*(int*)(&Cbase) + 3); fun(); fun = (Fun) * ((int*)*(int*)(&Cbase) + 4); fun(); fun = (Fun) * ((int*)*(int*)(&Cbase) + 5); fun(); return; } ``` ![image.png](http://www.irohane.top/usr/uploads/2021/03/235073972.png) ## 有覆盖继承 如下图所示,在子类虚函数表中将继承过来父类虚函数表内对应函数的地址给替换为了自己函数地址 ![image.png](http://www.irohane.top/usr/uploads/2021/03/534255173.png) ## 多继承无覆盖 基类+4位置为Base2继承来的虚函数表 基类+8的位置为Base3继承来的虚函数表 <span style="color:#00FFFF">注意!如果多继承无覆盖,使用的哪个基类指针,指向子类对象,使用的函数为该基类的函数,如果有覆盖的话优先使用子类的函数</span> ![image.png](http://www.irohane.top/usr/uploads/2021/03/142075710.png) ## 多继承有覆盖 <span style="color:#00FFFF">注意!如果多继承无覆盖,使用的哪个基类指针,指向子类对象,使用的函数为该基类的函数,如果有覆盖的话优先使用子类的函数</span> 如果有相同虚函数,子类重写将会把所继承父类对应的虚函数地址全部修改 ![image.png](http://www.irohane.top/usr/uploads/2021/03/1317222898.png) 最后修改:2021 年 03 月 04 日 © 来自互联网 赞 0 如果觉得我的文章对你有用,请随意赞赏