【深度探索c++对象模型】Function语义学之虚函数
虚函数的一般实现模型:每一个class有一个virtual table,内含该class中的virtual function的地址,然后每个object有一个vptr,指向virtual table。
识别class是否支持多态,唯一恰当的方法是看它是否有virtual function,只要class拥有virtual function,它就需要额外的执行期信息(以下两点)。
考虑ptr->z(),ptr是基类指针,z是虚函数,为了找到并调用z()的适当实体,我们需要两项信息:
1.ptr所指对象的真实类型(ptr的动态类型,在运行时决定)
2.z()实体位置(放在virtual table中)
如何实现上述信息?
在每个多态的class object身上添加两个members:
1.一个字符串或数字,表示class的类型
2.一个指针,指向某表格,表格中带有程序的虚函数的执行期地址
在c++中,虚函数可以在编译时获知,它们地址是固定不变的,放在virtual table中。为了找到virtual table,每个class object被安插一个由编译器内部产生的指针,指向该表格。为了找到函数地址,每一个虚函数被指派一个表格索引值。以上的工作由编译器完成,程序运行时要完成的是在特定的virtual table slot(记录着虚函数地址)中激活虚函数。
(编译时把每个类的虚函数地址放到该类对应的virtual table中,运行时根据指针或引用的动态类型在对应的virtual table中找到目标函数并调用)
一个class只有一个virtual table,每个table内含其对应的class object中所有active virtual function的地址。这些active virtual function包括:
1.这个类所定义的函数实体,它会改写一个可能存在的base class virtual function函数实体(覆盖)
2.继承自base class的函数实体,这是在派生类不改写virtual function时才会出现的情况
当一个类继承自另一个类时,会发生以下三种情况:
1.它可以继承base class 所声明的virtual function的函数实体,即该函数实体的地址会被拷贝到派生类的virtual table相对应的slot中
2.它可以使用自己的函数实体,该函数实体覆盖了父类的虚函数(覆盖)
3.它可以加入一个新的虚函数,这时候virtual table的尺寸会增加一个slot,而新的函数实体地址会被放进该slot之中
----------------------------------------------------------------------------------------------------------------------------------
虚函数表详解(转载)
一般继承(无虚函数覆盖)

对于实例Derive d的虚函数表如下:

我们可以看到:
1)虚函数按照其声明顺序放入表中
2)父类的虚函数在子类的虚函数前面
一般继承(有虚函数覆盖)

对于派生类的实例,其虚函数表如下:
我们从表中可以看到下面几点,
1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。
2)没有被覆盖的函数依旧。
这样,我们就可以看到对于下面这样的程序,
Base *b = new Derive();
b->f();
b的动态类型是派生类指针,在派生类的虚函数表中查找Derive函数,而在该表中Derive函数为派生类函数实体地址,所以调用的是派生类函数。由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。
多重继承(无虚函数覆盖)

对于子类实例中的虚函数表,是下面这个样子:
我们可以看到:
1) 每个父类都有自己的虚表。
2) 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的),即子类有多个虚表,分别对应不同的父类,自己自定义的虚函数放在第一个虚表中
这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。
多重继承(有虚函数覆盖)

下面是对于子类实例中的虚函数表的图:

我们可以看见,三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,我们就可以任一静态类型的父类来指向子类,并调用子类的f()了。
class a
{
public:
virtual void f1(){}
}; class b
{
public:
virtual void f2(){}
}; class c
{
public:
virtual void f3(){}
}; class d: public a, public b, public c
{
public:
virtual void f4(){}
}; void main()
{
cout << sizeof(a) << " " << sizeof(b) << " " << sizeof(c) <<" "<<sizeof(d)<< endl;
system("pause");
}
vs2013输出为4,4,4,12
安全性
一、通过父类型的指针访问子类自己的虚函数
任何妄图使用父类指针想调用子类中的独有的的成员函数的行为都会被编译器视为非法,因为这些成员不在父类指针所指内存空间里,编译器是根据指针的静态类型来确定调用对象的地址空间的,所以,这样的程序根本无法编译通过。但在运行时,我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。
二、访问non-public的虚函数
另外,如果父类的虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,所以,我们(类的用户不能访问类的private或protected成员)同样可以使用访问虚函数表的方式来访问这些non-public的虚函数,这是很容易做到的。
【深度探索c++对象模型】Function语义学之虚函数的更多相关文章
- 深度探索C++对象模型第四章:函数语义学
C++有三种类型的成员函数:static/nonstatic/virtual 一.成员的各种调用方式 C with class 只支持非静态成员函数(Nonstatic member function ...
- [读书系列] 深度探索C++对象模型 初读
2012年底-2014年初这段时间主要用C++做手游开发,时隔3年,重新拿起<深度探索C++对象模型>这本书,感觉生疏了很多,如果按前阵子的生疏度来说,现在不借助Visual Studio ...
- 深度探索C++对象模型
深度探索C++对象模型 什么是C++对象模型: 语言中直接支持面向对象程序设计的部分. 对于各个支持的底层实现机制. 抽象性与实际性之间找出平衡点, 需要知识, 经验以及许多思考. 导读 这本书是C+ ...
- 《深度探索C++对象模型》读书笔记(一)
前言 今年中下旬就要找工作了,我计划从现在就开始准备一些面试中会问到的基础知识,包括C++.操作系统.计算机网络.算法和数据结构等.C++就先从这本<深度探索C++对象模型>开始.不同于& ...
- c++学习书籍推荐《深度探索C++对象模型》下载
百度云及其他网盘下载地址:点我 百度云及其他网盘下载地址:点我 编辑推荐 如果你是一位C++程序员,渴望对于底层知识获得一个完整的了解,那么这本<深度探索C++对象模型>正适合你 作者简介 ...
- 读书笔记《深度探索c++对象模型》 概述
<深度探索c++对象模型>这本书是我工作一段时间后想更深入了解C++的底层实现知识,如内存布局.模型.内存大小.继承.虚函数表等而阅读的:此外在很多面试或者工作中,对底层的知识的足够了解也 ...
- 拾遗与填坑《深度探索C++对象模型》3.3节
<深度探索C++对象模型>是一本好书,该书作者也是<C++ Primer>的作者,一位绝对的C++大师.诚然该书中也有多多少少的错误一直为人所诟病,但这仍然不妨碍称其为一本好书 ...
- 柔性数组-读《深度探索C++对象模型》有感 (转载)
最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...
- 柔性数组-读《深度探索C++对象模型》有感
最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...
随机推荐
- less算宽度 加~ width: calc(~"50% - 35px");
less算宽度 加~ width: calc(~"50% - 35px");
- android 图片
1,setCompoundDrawables(Drawable left, Drawable top,Drawable right, Drawable bottom) 设置图片出现在textView, ...
- (5) openssl speed(测试算法性能)和openssl rand(生成随机数)
1.1 openssl speed 测试加密算法的性能 支持的算法有: openssl speed [md2] [mdc2] [md5] [hmac] [sha1] [rmd160] [idea-cb ...
- 实现验证的vsftpd虚拟用户
实现基于文件验证的vsftpd虚拟用户--(一台) 一.创建用户数据库文件 vim /etc/vsftpd/vuser cd /etc/vsftpd/ db_load -T -t hash -f vu ...
- day23 01 类的命名空间
day23 01 类的命名空间 一.初识面向对象复习 定义类: class 函数:方法 动态属性 变量:类属性 静态属性 过程: (1)_init_方法:初始化:def _init_(self,参数 ...
- 【HDU 2028】Lowest Common Multiple Plus
Problem Description 求n个数的最小公倍数. Input 输入包含多个测试实例,每个测试实例的开始是一个正整数n,然后是n个正整数. Output 为每组测试数据输出它们的最小公倍数 ...
- Android开发——查询/卸载手机里的应用、应用图标创建
1. 获取手机里的所有已安装的应用 以前写过一个SoftProviderUtil工具类,拿出来分享一个.通过PackageManager,不仅可以获取PackageName,判断此进程是否为系统应用, ...
- jQuery.data() 的实现方式
jQuery.data() 的作用是为普通对象或 DOM Element 附加(及获取)数据. 下面将分三个部分分析其实现方式: 1. 用name和value为对象附加数据:即传入三个参数,第 ...
- 局域网虚拟机端口映射访问apache
如果我们在虚拟机内搭建好服务器后,希望可以在局域网内的设备上都能访问到这个虚拟服务器,就可以参照以下步骤来操作.其中包括了很多遇到的坑.先说说我的环境是 宿主机:windows 8.1 虚拟机:vmw ...
- tarjan求割边割点
tarjan求割边割点 内容及代码来自http://m.blog.csdn.net/article/details?id=51984469 割边:在连通图中,删除了连通图的某条边后,图不再连通.这样的 ...