c++虚函数与虚表深度解析
来源:互联网 发布:开淘宝的心得体会 编辑:程序博客网 时间:2024/05/20 00:13
一.虚函数是怎么被找到的
首先来看一下单继承,编译下面的代码:
#include<iostream>#include<string.h>using namespace std;class Grandam{public: virtual void introduce_self() { cout << "I am grandam." << endl; }};class Mother:public Grandam{public: virtual void introduce_self() { cout << "I am mother." << endl; }};class Daughter :public Mother{public: void introduce_self() { cout << "I am daughter." << endl; }};int main(){ Grandam* ptr; Grandam g; Mother m; Mother m1; Daughter d; typedef void(*introduce_self)(); ptr = &g; ptr->introduce_self(); ptr = &m; ptr->introduce_self(); ptr = &m1; ptr = &d; ptr->introduce_self(); introduce_self* p = (introduce_self*)(*(int*)(&g)); (*p)(); system("pause"); return 0;}
结果如下图所示:
第四个结果就是调用的是基类Grandam中的虚函数。
代码中:
typedef void(*introduce_self)(); //对函数进行重命名introduce_self* p = (introduce_self*)(*(int*)(&g)); //定义一个函数指针,并让它指向对象内存的首地址里存放的地址(即虚表指针或地址)(*p)(); //对虚表调用该函数因为虚表里存放的是虚函数的地址,所以对函数指针进行解引用,即就是调用该虚函数。
从而我们得知:
只要存在虚函数,那么被基类指针或被基类对象引用的对象中调用该虚函数时,该被调用对象的首地址存放的是虚表指针_vfptr(地址),而虚表_vfptr中存放的是虚函数地址。
二.基类、派生类是否共用一个虚表
我们也可以按f10对它进行单步调试,看内存中存放内容的变化, 如图所示:
1. 对基类Grandam对象g的函数introduce_self()调用时的内存如下图所示:
2. 对派生类Mother对象m的函数introduce_self()调用时的内存如下图所示:
3. 对派生类Mother对象m的函数introduce_self()调用时的内存如下图所示:
4. 对派生类Daughter对象d的函数introduce_self()调用时的内存如下图所示:
总结:根据三个内存中的对比我们知道,不同对象地址中存放的虚表指针不同,而同类对象共用一个虚表。
三.基类、派生类调用虚函数的顺序
1. 单继承,代码如下:
#include<iostream>#include<string.h>using namespace std;class Grandam{public: void introduce_self1() { cout << "1: I am grandam." << endl; } void introduce_self2() { cout << "2: I am grandam." << endl; } virtual void introduce_self3() { cout << "3: I am grandam." << endl; } virtual void introduce_self4() { cout << "4: I am grandam." << endl; }};class Daughter :public Grandam{public: void introduce_self2() { cout << "2: I am daughter." << endl; } virtual void introduce_self5() { cout << "5: I am daughter." << endl; } virtual void introduce_self1() { cout << "1: I am daughter." << endl; } virtual void introduce_self4() { cout << "4: I am daughter." << endl; }};int main(){ Grandam g; Daughter d; Daughter* ptr = &d; typedef void(*intro)(); intro* pi = (intro*)(*((int*)(&d))); while (*pi) { (*pi)(); (int *)pi++; } return 0;}
**下面这段代码,是对虚表中虚函数的调用,注意事项如下分析:
typedef void(*intro)(); intro* pi = (intro*)(*((int*)(&d))); while (*pi) { (*pi)(); (int *)pi++; }
注意: 是对派生类对象d的虚表的调用,这是在vs 2012下运行的,因为虚表的最后一个地址为00000000,所以可写成此while循环,不同的编译器,有不同的写法。
运行结果如下图:
总结我们可以看出单继承下:
按照对象地址中存放的虚表指针 :虚表里面存放虚函数的地址顺序, 去调用虚函数:
1).首先调用基类中的虚函数,按在基类中声明的顺序调用。
2).基类的虚函数调用完后,再调用派生类中自带的虚函数,放在基类函数的后面。
3).调用派生类的虚函数时,也是按照在派生类中声明顺序调用的。
2.多继承如下代码:
#include<iostream>#include<string.h>using namespace std;class Grandam{public: void introduce_self1() { cout << "1: I am grandam." << endl; } void introduce_self2() { cout << "2: I am grandam." << endl; } virtual void introduce_self3() { cout << "3: I am grandam." << endl; } virtual void introduce_self4() { cout << "4: I am grandam." << endl; }};class Mother {public: virtual void introduce_self1() { cout << "1: I am mother." << endl; } virtual void introduce_self2() { cout << "2: I am mother." << endl; }};class Daughter :public Grandam , public Mother{public: void introduce_self2() { cout << "2: I am daughter." << endl; } virtual void introduce_self5() { cout << "5: I am daughter." << endl; } void introduce_self1() { cout << "1: I am daughter." << endl; } virtual void introduce_self4() { cout << "4: I am daughter." << endl; }};int main(){ Grandam g; Mother m; Daughter d; Mother& m1 = d; Grandam& g1 = d; typedef void(*pfun)(); pfun* pg1 = (pfun*)(*(int*)(&g1)); while (*pg1) { (*pg1)(); (int*)pg1++; } pfun* pm1 = (pfun*)(*(int*)(&m1)); while (*pm1) { (*pm1)(); (int*)pm1++; } return 0;}
结果如下图:
总结:多继承情况下,派生类自有的虚函数 在调用完第一个基类的虚函数 后面调用。
0 0
- c++虚函数与虚表深度解析
- C++虚函数表深度解析
- C++虚函数表深度解析
- c++虚函数深度解析
- 深度解析函数调用与变量存储
- 宏与内联函数 深度解析
- 【c++】大牛解析虚函数表
- 虚函数表深度分析
- 虚函数表深度分析- -
- 虚函数表深度剖析
- ftok()函数深度解析
- 深度解析C申明
- C语言深度解析
- 【C/C++和指针】深度解析---指针与数组 【精华】
- 【C/C++和指针】深度解析---指针与数组 【精华】
- 【C/C++和指针】深度解析---指针与数组 【精华】
- 深度学习术语表与解析
- c++--多态性与虚函数
- c/c++整理--引用和指针(5)
- Training a perceptron via scikit-learn
- C++之友元函数总结
- 2017第八届蓝桥杯总结
- leetcode题解-485. Max Consecutive Ones
- c++虚函数与虚表深度解析
- TableView去除sectionheader悬浮效果,设置Group去除顶部留白
- JVM内存管理------杂谈(借此也论一论obj=null)
- pat甲级1008. Elevator (20)
- 概率DP——HDU 5236
- Leetcode46——Permutations
- JVM内存管理------GC简介
- SPOJ ALL All Discs Considered 搜索
- Mac上Numbers显示.csv中文乱码问题(已解决)