虚函数的一般实现模型:每一个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语义学之虚函数的更多相关文章

  1. 深度探索C++对象模型第四章:函数语义学

    C++有三种类型的成员函数:static/nonstatic/virtual 一.成员的各种调用方式 C with class 只支持非静态成员函数(Nonstatic member function ...

  2. [读书系列] 深度探索C++对象模型 初读

    2012年底-2014年初这段时间主要用C++做手游开发,时隔3年,重新拿起<深度探索C++对象模型>这本书,感觉生疏了很多,如果按前阵子的生疏度来说,现在不借助Visual Studio ...

  3. 深度探索C++对象模型

    深度探索C++对象模型 什么是C++对象模型: 语言中直接支持面向对象程序设计的部分. 对于各个支持的底层实现机制. 抽象性与实际性之间找出平衡点, 需要知识, 经验以及许多思考. 导读 这本书是C+ ...

  4. 《深度探索C++对象模型》读书笔记(一)

    前言 今年中下旬就要找工作了,我计划从现在就开始准备一些面试中会问到的基础知识,包括C++.操作系统.计算机网络.算法和数据结构等.C++就先从这本<深度探索C++对象模型>开始.不同于& ...

  5. c++学习书籍推荐《深度探索C++对象模型》下载

    百度云及其他网盘下载地址:点我 百度云及其他网盘下载地址:点我 编辑推荐 如果你是一位C++程序员,渴望对于底层知识获得一个完整的了解,那么这本<深度探索C++对象模型>正适合你 作者简介 ...

  6. 读书笔记《深度探索c++对象模型》 概述

    <深度探索c++对象模型>这本书是我工作一段时间后想更深入了解C++的底层实现知识,如内存布局.模型.内存大小.继承.虚函数表等而阅读的:此外在很多面试或者工作中,对底层的知识的足够了解也 ...

  7. 拾遗与填坑《深度探索C++对象模型》3.3节

    <深度探索C++对象模型>是一本好书,该书作者也是<C++ Primer>的作者,一位绝对的C++大师.诚然该书中也有多多少少的错误一直为人所诟病,但这仍然不妨碍称其为一本好书 ...

  8. 柔性数组-读《深度探索C++对象模型》有感 (转载)

    最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...

  9. 柔性数组-读《深度探索C++对象模型》有感

    最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下 ...

随机推荐

  1. 1.1 Qt入门

    学习Qt的前提是学好C++. 我刚入门Qt,打算趁着暑假2个月时间来学习<C++ GUI Qt 4>这本书. 现在有Qt4和Qt5,似乎很多公司都还是在使用Qt4,所以我也就选择了学习Qt ...

  2. OpenCV2:第三章 读取图像

    一.简介 将图像文件读入内存,可以用cv::imread()函数 二.读取图像 Mat imread(const string& filename,int flags=1); Mat: 如果读 ...

  3. 初见Vue

    一.What 官方定义:是一套用于构建用户界面的渐进式框架.这,what?不明觉厉,我反正现在还是不知道,在这之前,就只知道Vue.js是用来渲染数据的,其实它的核心库只关注视图层.不多说,用多了就知 ...

  4. C++位域和内存对齐问题

    1. 位域: 1. 在C中,位域可以写成这样(注:位域的数据类型一律用无符号的,纪律性). struct bitmap { unsigned a : ; unsigned b : ; unsigned ...

  5. 「 HDOJ P3887 」 Counting Offspring

    翻译 题目描述 给你一棵树,和它的树根 $P$,并且节点从 $1\rightarrow n$ 编号,现在定义 $f(i)$ 为 $i$ 的子树中,节点编号小于 $i$ 的节点的个数. 输入格式 有多组 ...

  6. Linux制作本地yum

    首先在vm上安装centos 1.首先查看挂载光盘的位置:#df -h [root@lang ~]# df -hFilesystem                   Size  Used Avai ...

  7. (1) LVS基本概念和三种模式

    网站架构中,负载均衡技术是实现网站架构伸缩性的主要手段之一. 所谓"伸缩性",是指可以不断向集群中添加新的服务器来提升性能.缓解不断增加的并发用户访问压力.通俗地讲,就是一头牛拉不 ...

  8. Centos 7安装Mysql5.7

    1.下载(国内镜像,比搜狐的快一点):http://mirrors.ustc.edu.cn/mysql-ftp/Downloads/MySQL-5.7/mysql-5.7.22-linux-glibc ...

  9. POJ 3310 Caterpillar(图的度的判定)

    题意: 给定一幅图, 问符不符合一下两个条件: (1) 图中没有环 (2)图中存在一条链, 点要么在链上, 要么是链上点的邻居. 分析: 建图,记录度数, 去掉所有度为1的点, 然后看看剩下是否是有2 ...

  10. 【ORACLE】查看死锁进程并结束死锁的脚本

    --共享锁:Share:排他锁:Exclusive:行共享锁:Row-S:行排他锁:Row-X select V$SESSION.sid,v$session.SERIAL#,v$process.spi ...