多态性可分为两类:静态多态和动态多态。函数重载和运算符重载实现的多态属于静态多态,动态多态性是通过虚函数实现的。

每个含有虚函数的类有一张虚函数表(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++虚函数与虚函数表的更多相关文章

  1. C++虚函数和虚函数表

    前导 在上面的博文中描述了基类中存在虚函数时,基类和派生类中虚函数表的结构. 在派生类也定义了虚函数时,函数表又是怎样的结构呢? 先看下面的示例代码: #include <iostream> ...

  2. C++虚函数及虚函数表解析

    一.背景知识(一些基本概念) 虚函数(Virtual Function):在基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数.纯虚函数(Pure Virtual Functio ...

  3. C++ 由虚基类 虚继承 虚函数 到 虚函数表

    //虚基类:一个类可以在一个类族中既被用作虚基类,也被用作非虚基类. class Base1{ public: Base1(){cout<<"Construct Base1!&q ...

  4. 20140321 sizeof 虚函数与虚函数表 静态数组空间 动态数组空间 位字段

    1.静态的数组空间char a[10];sizeof 不能用于1:函数类型 2:动态的数组空间new3:位字段 函数类型:int fun();sizeof(fun())计算的是返回类型的大小,并不是函 ...

  5. c++ 虚函数多态、纯虚函数、虚函数表指针、虚基类表指针详解

    静态多态.动态多态 静态多态:程序在编译阶段就可以确定调用哪个函数.这种情况叫做静态多态.比如重载,编译器根据传递给函数的参数和函数名决定具体要使用哪一个函数.动态多态:在运行期间才可以确定最终调用的 ...

  6. 为何JAVA虚函数(虚方法)会造成父类可以"访问"子类的假象?

      首先,来看一个简单的JAVA类,Base. 1 public class Base { 2 String str = "Base string"; 3 protected vo ...

  7. C++基础知识 基类指针、虚函数、多态性、纯虚函数、虚析构

    一.基类指针.派生类指针 父类指针可以new一个子类对象 二.虚函数 有没有一个解决方法,使我们只定义一个对象指针,就可以调用父类,以及各个子类的同名函数? 有解决方案,这个对象指针必须是一个父类类型 ...

  8. virtual之虚函数,虚继承

    当类中包含虚函数时,则该类每个对象中在内存分配中除去数据外还包含了一个虚函数表指针(vfptr),指向虚函数表(vftable),虚函数表中存放了该类包含的虚函数的地址. 当子类通过虚继承的方式从父类 ...

  9. C++纯虚函数、虚函数、实函数、抽象类,重载、重写、重定义

    首先,面向对象程序设计(object-oriented programming)的核心思想是数据抽象.继承.动态绑定.通过数据抽象,可以使类的接口与实现分离,使用继承,可以更容易地定义与其他类相似但不 ...

  10. 虚函数&纯虚函数&抽象类&虚继承

    C++ 虚函数&纯虚函数&抽象类&接口&虚基类   1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过 ...

随机推荐

  1. 关于Guid

    GUID(全局统一标识符)是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的.通常平台会提供生成GUID的API.生成算法很有意思,用到了以太网卡地址.纳秒级时间.芯片ID码和许多可 ...

  2. WebApi与手机客户端通信安全机制

    最近公司有几个项目需要开发手机客户端,服务器端选用WebApi,那么如何保证手机客户端在请求服务器端时数据不被篡改,如何保证一个http请求的失效机制,下面总结一下我们在项目中针对这两个问题的解决方案 ...

  3. Arcgis for Javascript 在VS2012中的智能提示

    官方地址: https://developers.arcgis.com/en/javascript/jsapi/api_codeassist.html 安装步骤 Visual Studio 2010 ...

  4. 在express站点中使用ejs模板引擎

    在express站点中使用ejs模板引擎 文/玄魂 目录 在express站点中使用ejs模板引擎 前言 1.1         安装 1.2修改app.js 1.3创建测试页面 前言 使用 vs创建 ...

  5. Microsoft 家族新成员 Datazen 移动BI 介绍

    开篇介绍 Microsoft 在上个月即 2015年4月份收购了 Datazen www.datazen.com, Datazen 专注于移动 BI 和数据可视化领域,并且它的基本部署与配置架构都是基 ...

  6. GCC选项

    -g: Debugging Option. 提供给GDB的debugging信息的选项: -fno-omit-frame-pointer: Optimization Option: -Wstrict- ...

  7. iOS——Swift开发中的单例设计模式(摘译,非原创)

    最近在开发一个小的应用,遇到了一些Objective-c上面常用的单例模式,但是swift上面还是有一定区别的,反复倒来倒去发现不能按常理(正常的oc to swift的方式)出牌,因此搜索了一些帖子 ...

  8. 看看这蛋疼的Java代码

    项目上要基于现有代码开发,却碰到了很多让人蛋疼的代码.例如下面这个,大家看看能找到多少槽点: public static String addDate(String date, String into ...

  9. 利用模拟退火提高Kmeans的聚类精度

    http://www.cnblogs.com/LBSer/p/4605904.html Kmeans算法是一种非监督聚类算法,由于原理简单而在业界被广泛使用,一般在实践中遇到聚类问题往往会优先使用Km ...

  10. Django实现一个相片管理系统01

    有些日子没写笔记,O(∩_∩)O哈哈~实在是肚子没有墨水啦!今天不写数据结构啦!多怀念研究数据结构的日子啊! 可是呢!最近有个项目要搞图像管理方面的,具体内容就不说啦!我们今天来实现一个简单的相册管理 ...