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. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过 ...
随机推荐
- 在线教学、视频会议 Webus Fox(3) 客户端开发手册
本文主要介绍webus fox 客户端的配置及接口说明. 1. 文件列表和配置 1.1 文件列表 1.2 common.xml 配置 根据服务器端的部署, 替换[ServerUrl] , [RtmpP ...
- vim安装YouCompleteMe 插件
要安装YouCompleteMe ,vim须支持python.看是否支持,可以在vim中:version 查看, 如果python前有+号,就是支持,减号就是不支持. 如果不支持,需要以编译安装方式重 ...
- [ACM_图论] Domino Effect (POJ1135 Dijkstra算法 SSSP 单源最短路算法 中等 模板)
Description Did you know that you can use domino bones for other things besides playing Dominoes? Ta ...
- EF结合三层:三层中数据层父类和业务层父类的使用
今天我们主要讨论下数据层父类和业务层父类的使用.众所周知,数据层无非就是实现增删改查的方法.无论是哪个实体类,无非就是为了实现增删改查方法,所有我们在三层的DAL层封装了一个BaseDAL类,来做增删 ...
- Backbone事件管理——Backbone.Events模块API结构
模块Backbone.Events的事件管理是通过Backbone提供的Events API来实现的,该API在1.0版本之前仅仅提供了几个基本的方法,如on.off.trigger.once分别执行 ...
- MySQL笔记汇总
[目录] MySQL笔记汇总 一.mysql简介 数据简介 结构化查询语言 二.mysql命令行操作 三.数据库(表)更改 表相关 字段相关 索引相关 表引擎操作 四.数据库类型 数字型 字符串型 日 ...
- SQL中order by;group up;like;关联查询join on的用法
排序order by的用法: 1.order by 字段名1 asc/desc, 字段名2 asc/desc,... 先按照字段名1的升序/降续给表进行排列 然后 按照字段名2的升序/降续给表进行排列 ...
- C语言实现二叉树-利用二叉树统计单词数目
昨天刚参加了腾讯2015年在线模拟考: 四道大题的第一题就是单词统计程序的设计思想: 为了记住这一天,我打算今天通过代码实现一下: 我将用到的核心数据结构是二叉树: (要是想了解简单二叉树的实现,可以 ...
- paip. 混合编程的实现resin4 (自带Quercus ) 配置 php 环境
paip. 混合编程的实现resin4 (自带Quercus ) 配置 php 环境 #---混合编程的类型 1.代码inline 方式 2.使用库/api 解析方式. #----配置resin 支 ...
- 分析Linux内核中进程的调度(时间片轮转)-《Linux内核分析》Week2作业
1.环境的搭建: 这个可以参考孟宁老师的github:mykernel,这里不再进行赘述.主要是就是下载Linux3.9的代码,然后安装孟宁老师编写的patch,最后进行编译. 2.代码的解读 课上的 ...