首页 / C++ / C++中的虚函数以及虚函数表
C++中的虚函数以及虚函数表
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了C++中的虚函数以及虚函数表,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含6650字,纯文字阅读大概需要10分钟。
内容图文
一.虚函数的定义
被virtual关键字修饰的成员函数,目的是为了实现多态
ps:
关于多态【接口和实现分离,父类指针指向子类的实例,然后通过父类指针调用子类的成员函数,这样可以让父类指针拥有多种形态,所以称之为多态】
二.虚函数表
该表为一个类的虚函数的地址表,用于解决继承和覆盖的问题
1.拥有虚函数的类才有虚函数表
2.虚函数表属于类,然后类的所有对象通过虚函数表指针共享类的虚函数表
3.虚函数表的作用:当使用父类指针来操作子类对象时,虚函数表就像一个地图一样,指明了实际所应该调用的函数
4.c++编译器保证虚函数表的指针存在于对象实例中最前面的位置(为了保证在多层继承或者多重继承的情况下获得函数表的性能),这意味着我们可以通过对象实例的地址得到虚函数表,然后就可以遍历其中的虚函数指针,并且调用响应的虚函数
ps:多重继承:多个父类,多层继承:父类还存在父类
【通过虚函数表,遍历虚函数指针,调用响应的虚函数】
#include<bits/stdc++.h> usingnamespace std; class Base { public: virtualvoid f() { cout << "Base::f" << endl; } virtualvoid g() { cout << "Base::g" << endl; } virtualvoid h() { cout << "Base::h" << endl; } }; typedef void(*Fun)(void); Base b; Fun pFun = NULL; int main() { cout << "虚函数表地址:" << (int*)(&b) << endl; cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl; //通过虚函数表调用虚函数 pFun = (Fun)*((int*)*(int*)(&b)); // Base::f() pFun(); pFun =(Fun)*((int*)*(int*)(&b)+1); // Base::g() pFun(); pFun =(Fun)*((int*)*(int*)(&b)+2); // Base::h() pFun(); }
结果:
虚函数表地址:0x477008 虚函数表 — 第一个函数地址:0x473668 Base::f Base::g Base::h
以上为无继承情况
1.单层继承无虚函数覆盖的情况
1)虚函数按照声明顺序放入表中
2)父类虚函数在前,子类虚函数在后
3)末尾点号为虚函数表的结尾标识符,在不同编译器下值不同
#include<bits/stdc++.h> usingnamespace std; class Base { public: virtualvoid f() { cout << "Base::f" << endl; } virtualvoid g() { cout << "Base::g" << endl; } virtualvoid h() { cout << "Base::h" << endl; } }; class Base_son:public Base { public: virtualvoid f1() { cout << "Base_son::f1" << endl; } virtualvoid g1() { cout << "Base_son::g1" << endl; } virtualvoid h1() { cout << "Base_son::h1" << endl; } }; typedef void(*Fun)(void); Base_son d; Fun pFun = NULL; int main() { cout << "虚函数表地址:" << (int*)(&d) << endl; cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&d) << endl; //通过虚函数表调用虚函数 pFun = (Fun)*((int*)*(int*)(&d)); // Base::f() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+1); // Base::g() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+2); // Base::h() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+3); // Base_son::f1() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+4); // Base_son::g1() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+5); // Base_son::h1() pFun(); return0; }
结果:
虚函数表地址:0x477008 虚函数表 — 第一个函数地址:0x473668 Base::f Base::g Base::h Base_son::f1 Base_son::g1 Base_son::h1
2.单层继承有虚函数覆盖的情况
1)覆盖的f()函数被放到了虚函数表中原父类虚函数的位置
2)没有被覆盖的函数没有变化
#include<bits/stdc++.h> usingnamespace std; class Base { public: virtualvoid f() { cout << "Base::f" << endl; } virtualvoid g() { cout << "Base::g" << endl; } virtualvoid h() { cout << "Base::h" << endl; } }; class Base_son:public Base { public: virtualvoid f() { cout << "Base_son::f" << endl; } virtualvoid g1() { cout << "Base_son::g1" << endl; } virtualvoid h1() { cout << "Base_son::h1" << endl; } }; typedef void(*Fun)(void); Base_son d; Fun pFun = NULL; int main() { cout << "虚函数表地址:" << (int*)(&d) << endl; cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&d) << endl; //通过虚函数表调用虚函数 pFun = (Fun)*((int*)*(int*)(&d)); // Base_son::f() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+1); // Base::g() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+2); // Base::h() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+3); // Base_son::g1() pFun(); pFun =(Fun)*((int*)*(int*)(&d)+4); // Base_son::h1() pFun(); return0; }
结果:
虚函数表地址:0x477008 虚函数表 — 第一个函数地址:0x473650 Base_son::f Base::g Base::h Base_son::g1 Base_son::h1
通过父类指针指向子类实例,子类覆盖父类方法,然后调用子类的方法,这样就实现了多态
3.多重继承无虚函数覆盖
1)每个父类都有自己的虚函数表
2)子类的虚函数被放到第一个父类的虚函数表中
这样做是为了解决不同的父类类型指针指向同一个子类实例,而能够调用到实际的函数
4.多重继承存在虚函数覆盖
1)父类虚函数表中被覆盖的虚函数全部被替换成了子类的覆盖虚函数
这样我们就通过父类指向子类从而访问子类的f()了
Derive d; Base1 *b1 = &d; Base2 *b2 = &d; Base3 *b3 = &d; b1->f(); //Derive::f() b2->f(); //Derive::f() b3->f(); //Derive::f() b1->g(); //Base1::g() b2->g(); //Base2::g() b3->g(); //Base3::g()
使用虚函数表可以做一些违反c++语义的事情:
1)通过父类指针访问子类自己的虚函数
子类的虚函数X在父类中没有,所以子类的虚函数X没有覆盖父类的虚函数,但是如果我们通过父类的指针来访问子类自己的虚函数的编译器会报错
Base1 *b1 = new Derive(); b1->f1(); //编译出错
但是我们通过虚函数表可以做到这种违背C++语义的事情:使用父类指针访问子类自己的虚函数
2)访问父类non-public的虚函数
如果父类的虚函数是private或protected的,但是这些feipublic的父类虚函数同样会存在于虚函数表中,所以我们可以通过访问虚函数表访问到这些虚函数
附上多重继承有虚函数覆盖的样例代码:
#include <iostream> usingnamespace std; class Base1 { public: virtualvoid f() { cout << "Base1::f" << endl; } virtualvoid g() { cout << "Base1::g" << endl; } virtualvoid h() { cout << "Base1::h" << endl; } }; class Base2 { public: virtualvoid f() { cout << "Base2::f" << endl; } virtualvoid g() { cout << "Base2::g" << endl; } virtualvoid h() { cout << "Base2::h" << endl; } }; class Base3 { public: virtualvoid f() { cout << "Base3::f" << endl; } virtualvoid g() { cout << "Base3::g" << endl; } virtualvoid h() { cout << "Base3::h" << endl; } }; class Derive : public Base1, public Base2, public Base3 { public: virtualvoid f() { cout << "Derive::f" << endl; } virtualvoid g1() { cout << "Derive::g1" << endl; } }; typedef void(*Fun)(void); int main() { Fun pFun = NULL; Derive d; int** pVtab = (int**)&d; //Base1‘s vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0); pFun = (Fun)pVtab[0][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1); pFun = (Fun)pVtab[0][1]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2); pFun = (Fun)pVtab[0][2]; pFun(); //Derive‘s vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3); pFun = (Fun)pVtab[0][3]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[0][4]; cout<<pFun<<endl; //Base2‘s vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[1][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[1][1]; pFun(); pFun = (Fun)pVtab[1][2]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[1][3]; cout<<pFun<<endl; //Base3‘s vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[2][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[2][1]; pFun(); pFun = (Fun)pVtab[2][2]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[2][3]; cout<<pFun<<endl; return0; }
关于虚函数和普通函数:
1.类中的虚函数是动态生成的,由虚函数表的指向进行访问,不为类的对象分配内存,没有虚函数表,就无法访问虚函数
2.类中的普通函数静态生成,不为类的对象分配内存也可访问
参考:左耳朵耗子:C++虚函数表解析
原文:https://www.cnblogs.com/yinbiao/p/10987640.html
内容总结
以上是互联网集市为您收集整理的C++中的虚函数以及虚函数表全部内容,希望文章能够帮你解决C++中的虚函数以及虚函数表所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。