C++虚函数与虚函数表
多态性可分为两类:静态多态和动态多态。函数重载和运算符重载实现的多态属于静态多态,动态多态性是通过虚函数实现的。
每个含有虚函数的类有一张虚函数表(vtbl),表中每一项是一个虚函数的地址, 也就是说,虚函数表的每一项是一个虚函数的指针。
没有虚函数的C++类,是不会有虚函数表的。
两张图:
简单例子:
1 #include <iostream>
2 #include <windows.h>
3
4 using namespace std;
5
6 class base
7 {
8 virtual void f(){cout<<"base::f"<<endl;};
9 virtual void g(){cout<<"base::g"<<endl;};
10 virtual void h(){cout<<"base::h"<<endl;};
11 };
12
13 typedef void (*pfun)();
14
15 void main()
16 {
17 DWORD w=0x4011e0; //虚函数表第一项的内容,也就是第一个虚函数的地址
18
19 pfun fun=NULL;
20 base b;
21 base *pbase=&b;
22
23 fun=(pfun)w;
24 fun(); //调用第一个虚函数
25 }
查看对象b在内存中:
查看虚函数表:
虚函数表的指针4个字节大小(vptr),存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
虚函数表的结束标志在不同的编译器下是不同的。在VC6.0下,这个值是NULL,如图:
另一个例子:
1 #include <iostream>
2
3 using namespace std;
4
5 class base
6 {
7 virtual void f(){cout<<"base::f"<<endl;};
8 virtual void g(){cout<<"base::g"<<endl;};
9 virtual void h(){cout<<"base::h"<<endl;};
10 };
11
12 class Derive : public base
13 {
14
15 public:
16 Derive(){};
17 virtual void f() { cout << "Derive::f" << endl; }
18 virtual void g() { cout << "Derive::g" << endl; }
19
20 };
21
22 typedef void(*pfun)();
23
24 void main()
25 {
26 pfun fun=NULL;
27 Derive d;
28 base *p=&d;
29
30 fun=(pfun)**((int**)p);
31 fun(); //调用第一个虚函数
32
33 fun=(pfun)*(*((int**)p)+2);
34 fun(); //调用第三个函数
35
36 }
查看对象d在内存中:
多重继承:
有几个父类,就有几个vtab和vptr
代码:
1 #include <iostream>
2
3 using namespace std;
4
5 class Base1 {
6
7 public:
8
9 virtual void f() { cout << "Base1::f" << endl; }
10
11 virtual void g() { cout << "Base1::g" << endl; }
12
13 virtual void h() { cout << "Base1::h" << endl; }
14
15
16
17 };
18
19 class Base2 {
20
21 public:
22
23 virtual void f() { cout << "Base2::f" << endl; }
24
25 virtual void g() { cout << "Base2::g" << endl; }
26
27 virtual void h() { cout << "Base2::h" << endl; }
28
29 };
30
31
32 class Base3 {
33
34 public:
35
36 virtual void f() { cout << "Base3::f" << endl; }
37
38 virtual void g() { cout << "Base3::g" << endl; }
39
40 virtual void h() { cout << "Base3::h" << endl; }
41
42 };
43
44
45 class Derive : public Base1, public Base2, public Base3 {
46
47 public:
48
49 virtual void f() { cout << "Derive::f" << endl; }
50
51 virtual void g1() { cout << "Derive::g1" << endl; }
52
53 };
54
55
56 typedef void(*Fun)(void);
57
58 int main()
59
60 {
61
62 Fun pFun = NULL;
63
64 Derive d;
65
66 int** pVtab = (int**)&d;
67
68 //Base1's vtable
69
70 //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0);
71
72 pFun = (Fun)pVtab[0][0];
73
74 pFun();
75
76
77 //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1);
78
79 pFun = (Fun)pVtab[0][1];
80
81 pFun();
82
83
84 //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2);
85
86 pFun = (Fun)pVtab[0][2];
87
88 pFun();
89
90
91 //Derive's vtable
92
93 //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3);
94
95 pFun = (Fun)pVtab[0][3];
96
97 pFun();
98
99
100 //The tail of the vtable
101
102 pFun = (Fun)pVtab[0][4];
103
104 cout<<pFun<<endl;
105
106
107 //Base2's vtable
108
109 //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);
110
111 pFun = (Fun)pVtab[1][0];
112
113 pFun();
114
115
116 //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);
117
118 pFun = (Fun)pVtab[1][1];
119
120 pFun();
121
122
123 pFun = (Fun)pVtab[1][2];
124
125 pFun();
126
127
128 //The tail of the vtable
129
130 pFun = (Fun)pVtab[1][3];
131
132 cout<<pFun<<endl;
133
134
135 //Base3's vtable
136
137 //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);
138
139 pFun = (Fun)pVtab[2][0];
140
141 pFun();
142
143
144 //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);
145
146 pFun = (Fun)pVtab[2][1];
147
148 pFun();
149
150
151 pFun = (Fun)pVtab[2][2];
152
153 pFun();
154
155
156 //The tail of the vtable
157
158 pFun = (Fun)pVtab[2][3];
159
160 cout<<pFun<<endl;
161
162
163 cout<<sizeof(d)<<endl;
164
165 return 0;
166
167 }
C++虚函数与虚函数表的更多相关文章
- C++虚函数和虚函数表
前导 在上面的博文中描述了基类中存在虚函数时,基类和派生类中虚函数表的结构. 在派生类也定义了虚函数时,函数表又是怎样的结构呢? 先看下面的示例代码: #include <iostream> ...
- C++虚函数及虚函数表解析
一.背景知识(一些基本概念) 虚函数(Virtual Function):在基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数.纯虚函数(Pure Virtual Functio ...
- C++ 由虚基类 虚继承 虚函数 到 虚函数表
//虚基类:一个类可以在一个类族中既被用作虚基类,也被用作非虚基类. class Base1{ public: Base1(){cout<<"Construct Base1!&q ...
- 20140321 sizeof 虚函数与虚函数表 静态数组空间 动态数组空间 位字段
1.静态的数组空间char a[10];sizeof 不能用于1:函数类型 2:动态的数组空间new3:位字段 函数类型:int fun();sizeof(fun())计算的是返回类型的大小,并不是函 ...
- c++ 虚函数多态、纯虚函数、虚函数表指针、虚基类表指针详解
静态多态.动态多态 静态多态:程序在编译阶段就可以确定调用哪个函数.这种情况叫做静态多态.比如重载,编译器根据传递给函数的参数和函数名决定具体要使用哪一个函数.动态多态:在运行期间才可以确定最终调用的 ...
- 为何JAVA虚函数(虚方法)会造成父类可以"访问"子类的假象?
首先,来看一个简单的JAVA类,Base. 1 public class Base { 2 String str = "Base string"; 3 protected vo ...
- C++基础知识 基类指针、虚函数、多态性、纯虚函数、虚析构
一.基类指针.派生类指针 父类指针可以new一个子类对象 二.虚函数 有没有一个解决方法,使我们只定义一个对象指针,就可以调用父类,以及各个子类的同名函数? 有解决方案,这个对象指针必须是一个父类类型 ...
- virtual之虚函数,虚继承
当类中包含虚函数时,则该类每个对象中在内存分配中除去数据外还包含了一个虚函数表指针(vfptr),指向虚函数表(vftable),虚函数表中存放了该类包含的虚函数的地址. 当子类通过虚继承的方式从父类 ...
- C++纯虚函数、虚函数、实函数、抽象类,重载、重写、重定义
首先,面向对象程序设计(object-oriented programming)的核心思想是数据抽象.继承.动态绑定.通过数据抽象,可以使类的接口与实现分离,使用继承,可以更容易地定义与其他类相似但不 ...
- 虚函数&纯虚函数&抽象类&虚继承
C++ 虚函数&纯虚函数&抽象类&接口&虚基类 1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过 ...
随机推荐
- JBoss 系列四十八:JBoss 7/WildFly 使用TCP构建集群
我知道JBoss 集群Default 的设定就是UDP(JGroups),但在实际环境中的网络环境时常不允许UDP,在这种情况下,我们就需要使用TCP. JBoss 7/WildFly 中负责集群的主 ...
- Neo4j 2.0 M4 发布
Neo4j 发布了 2.0 的第四个里程碑版本,该版本要求 Java 7 的支持.详细的改进记录请看发行通知. Neo是一个网络——面向网络的数据库——也就是说,它是一个嵌入式的.基于磁盘的.具备完全 ...
- C#执行OracleHelper
/// <summary> /// 执行存储过程获取带有Out的参数 /// </summary> /// <param name="cmdText" ...
- jQuery document window load ready 区别详解
用过JavaScript的童鞋,应该知道window对象和document对象,也应该听说过load事件和ready事件,小菜当然也知道,而且自认为很了解,直到最近出了问题,才知道事情并不是那么简单. ...
- spring mvc ajax 提交复杂数组类型
The server refused this request because the request entity is in a format not supported by the reque ...
- java5 CountDownLatch同步工具
好像倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减1,当到达0时,所有等待者就开始执行. java.util.concurrent.CountDownLatch ...
- java开发常用jar包介绍(转载)
jta.jar 标准JTA API必要 commons-collections.jar 集合类 必要 antlr.jar ANother Tool for Language Recognition ...
- 地理围栏算法解析(Geo-fencing)
地理围栏算法解析 http://www.cnblogs.com/LBSer/p/4471742.html 地理围栏(Geo-fencing)是LBS的一种应用,就是用一个虚拟的栅栏围出一个虚拟地理边界 ...
- atitit.提升软件开发效率大的总结O5
atitit.提升软件开发效率大的总结O5 #---平台化.组件化 1 #--cbb公用模块的建设 1 #---内部最佳流程方法跟实践的总结 2 #---内部知识体系的建设 2 #---问题Qa库的建 ...
- 白条VS花呗,快餐式消费金融成巨头新战场
在这一次的国庆假期前,90后网红密子君吃空麦当劳事件引发了网友们的热议.短短半个小时,这位90后网红就吃光了25包薯条,随后又吃下两杯麦旋风,其疯狂举动引得四周食客纷纷围观拍照.那么,是什么刺激这位9 ...