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. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过 ...
随机推荐
- Sql语句,先查询再插入一条语句完成。
if ( (select COUNT(*) from Hr where 考勤号码 = '149' and 日期时间 = '2015/7/3 12:00:26') = 0 )INSERT INTO [ ...
- 使用ODP.NET一次执行多句SQL语句
在实际开发的时候有的时候希望一次执行多句SQL语句,又不想使用Transcation的话,可以直接将多句SQL语句拼接起来.例如: var sql = "Begin " + &qu ...
- console.log 被重写覆盖以后如何恢复
有时候一些项目中会使用类似如下的方式覆盖掉console对象: var console = {}; console.log = function(){}; console.info = functio ...
- Ora-01536:超出了表空间users的空间限量(转)
Ora-01536:超出了表空间users的空间限量(转) 正在开会,同事跑过来说数据库有问题,通讯程序不能入库,赶快获取一条insert into a values()语句后在toad工具中手动插入 ...
- 冲刺阶段 day 6
项目进展:学生管理这部分已经进展的差不多了,调试了几遍,改了几个小Bug之后就没有什么问题了,基本完成,如若后续还有一些新的功能到时候再自行加入.明天开始教师部分. 存在问题:我觉得我们的项目还是比较 ...
- git删除push到远程服务器的commit
如果不小心把不该提交的代码或者敏感的数据(如密码)提交到远程git服务器上,可以使用git reset回滚到上一个commit,并且commit history不留下任何痕迹. 具体做法: # 1.通 ...
- 利用模拟退火提高Kmeans的聚类精度
http://www.cnblogs.com/LBSer/p/4605904.html Kmeans算法是一种非监督聚类算法,由于原理简单而在业界被广泛使用,一般在实践中遇到聚类问题往往会优先使用Km ...
- Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)
介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间, ...
- 【软件架构】IM架构设计(安卓版)
1. 架构总览 2. 模块介绍 2.1 协议封装与任务流程 2.1.1 协议与任务的封装 协议有协议头(协议头因为格式相同,被抽象出来)和协议体组成,协议有两类:请求协议(request)和回复协议( ...
- Atitit.人力资源管理原理与概论
Atitit.人力资源管理原理与概论 1. 人力资源管理 第一章 人力资源管理概述 第二章 人力资源理论基础与发展演变 第三章 人力资源规划 第四章工作分析与工作设计 第五章 员工招聘与录用 第六章 ...