【深度探索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的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...
随机推荐
- MySQL存储过程实现分页及变量的定义
delimiter是MySQL中的命令,这个命令与存储过程没什么关系. 其实就是告诉mysql解释器,该段命令是否已经结束了,mysql是否可以执行了. 即改变输入结束符. 默认情况下,delimit ...
- iview upload on-format-error 事件 在 before-upload 事件 之后,导致在before里面阻止上传后,监测事件失效,需要自己手工写
iview upload on-format-error 事件 在 before-upload 事件 之后,导致在before里面阻止上传后,监测事件失效,需要自己手工写
- vscode 打开新文件不替换旧文件
设置 "workbench.editor.enablePreview": false
- 在Foxmail邮件客户端登录263企业邮箱
一.问题描述 首次用Foxmail登录263企业,输入账号和密码,创建 二.问题分析 客户端配置地址: 协议类型 服务器地址 默认端 加密端(SSL) POP pop.263.net 110 1995 ...
- vs 2012安装ASP.NET MVC5
VS2012能使用MVC5开发,但VS2012不自带MVC5,需要安装“用于 Visual Studio 2012 的 ASP.NET 和 Web 工具 2013.1” 从下面提供的链接下载安装: h ...
- 「 HDOJ P3887 」 Counting Offspring
翻译 题目描述 给你一棵树,和它的树根 $P$,并且节点从 $1\rightarrow n$ 编号,现在定义 $f(i)$ 为 $i$ 的子树中,节点编号小于 $i$ 的节点的个数. 输入格式 有多组 ...
- linux shell 自动判断操作系统release 然后连接FTP yum源的脚本
如何搭建本地yum源见附录① 如何搭建FTP yum源见附录② 脚本正文: #!/bin/sh# CenterOS config yumOSV=`rpm -q --qf %{version} cent ...
- 4 SQL 数据更新
4 数据更新 4-1 数据的插入(INSERT语句的使用方法) 通过create table语句创建出来的表,可以将其比作一个空空如也的箱子.只有把数据装入到这个箱子后,它才能称为数据库.用来装入数据 ...
- (9) tomcat中实现同一虚拟机中所有应用程序单点登录SSO
- Linux中的FTP服务
FTP服务 文件传输协议 FTPFile Transfer Protocol 早期的三个应用级协议之一 基于C/S结构 双通道协议:数据和命令连接 数据传输格式:二进制(默认)和文本 两种模式:服务器 ...