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. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过 ...
随机推荐
- FIM相关报错汇总
1.错误1:FIM在修改MA名字的时候报错: The management agent cannot be deleted or renamed because the working directo ...
- java的四种引用,强弱软虚
1.利用软引用和弱引用解决OOM问题:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的 ...
- SQL语句汇总(一)——数据库与表的操作以及创建约束
首先,非常感谢大家对上篇博文的支持,真是让本菜受宠若惊,同时对拖了这么久才出了此篇表示抱歉. 前言:此文旨在汇总从建立数据库到联接查询等绝大部分SQL语句.SQL语句虽不能说很多,但稍有时间不写就容易 ...
- 用c#开发微信 (12) 微统计 - 阅读分享统计系统 2 业务逻辑实现
微信平台自带的统计功能太简单,有时我们需要统计有哪些微信个人用户阅读.分享了微信公众号的手机网页,以及微信个人用户访问手机网页的来源:朋友圈分享访问.好友分享消息访问等.本系统实现了手机网页阅读.分享 ...
- 史无前例的Firefox奇怪问题:host中的common名称造成css文件无法加载
今天遭遇了一个非常非常奇怪的问题,一个css文件(common.cnblogs.com/Skins/marvin3/green.css),Firefox怎么也无法打开,一直在转圈. 而换成其它浏览器都 ...
- WinDbg 命令三部曲:(二)WinDbg SOS 扩展命令手册
本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载. 系列博文 <WinDbg 命令三部曲:(一)WinDbg 命令手册> <WinDb ...
- [ACM_暴力][ACM_几何] ZOJ 1426 Counting Rectangles (水平竖直线段组成的矩形个数,暴力)
Description We are given a figure consisting of only horizontal and vertical line segments. Our goal ...
- nginx upstream模块--负载均衡
Module ngx_http_upstream_module英文文档 upstream模块相关说明1.upstream模块应放于nginx.conf配置的http{}标签内2.upstream模块默 ...
- SONATYPE NEXUS搭建MAVEN私服
1.为什么使用Nexus如果没有私服,我们所需的所有构件都需要通过maven的中央仓库和第三方的Maven仓库下载到本地,而一个团队中的所有人都重复的从maven仓库下载构件无疑加大了仓库的负载和浪费 ...
- CSS/JS图片鼠标悬浮一道光闪过
看到有些网站logo鼠标悬浮上面的时候,会出现一道光,一闪而过,刚开始以为是gif图,已审查, 居然不是:现在就实现在这种效果: 先看看CSS实现的效果图: 看到没,就是这道刺眼的白光.... 啊 ...