单继承下的虚函数

虚函数的实现:

  • 为每个有虚函数的类配一张虚函数表(virtual table),它存储该类类型信息和所有虚函数执行期的地址。
  • 为每个有虚函数的类插入一个指针(vptr),这个指针指向该类的虚函数表。
  • 给每一个虚函数指派一个在表中的索引。

用这种模型来实现虚函数得益于在C++中,虚函数的地址在编译期是可知的,而且这一地址是固定不变的。而且表的大小不会在执行期增大或减小。

一个类的虚函数表中存储有类型信息(存储在索引为0的位置)和所有虚函数地址,这些虚函数地址包括三种:

  • 这个类定义的虚函数,会改写(overriding)一个可能存在的基类的虚函数实体——假如基类也定义有这个虚函数。
  • 继承自基类的虚函数实体,——基类定义有,而这个类却没有定义。直接继承之。
  • 一个纯虚函数实体。用来在虚函数表中占座,有时候也可以当做执行期异常处理函数。

每一个虚函数都被指派一个固定的索引值,这个索引值在整个继承体系中保持前后关联,例如,假如z()在Point虚函数表中的索引值为2,那么在Point3d虚函数表中的索引值也为2。

当一个类单继承自有虚函数的基类的时候,将按如下步骤构建虚函数表:

  1. 继承基类中声明的虚函数——这些虚函数的实体地址被拷贝到继承类中的虚函数表中对于的slot中。
  2. 如果有改写(override)基类的虚函数,那么在1中应将改写(override)的函数实体的地址放入对应的slot中而不是拷贝基类的。
  3. 如果有定义新的虚函数,那么将虚函数表扩大一个slot以存放新的函数实体地址

多重继承下的虚函数

在多重继承下,继承类需要为每一条继承线路维护一个虚函数表(也有可能这些表被合成为一个,但本质意义并没有变化)。当然这一切都发生在需要的情况下。

当使用第一继承的基类指针来调用继承类的虚函数的时候,与单继承的情况没有什么异样,问题出生在当以第二或后继的基类指针(或引用)的使用上。例如:

//假设有这样的继承关系:class Derived:public base1,public base2;
//base1,base2都定义有虚析构函数。
base2 *ptr = new derived;
//需要被转换为,这个转换在编译期完成
base2 *ptr = temp ? temp + sizeof(base1) : 0 ;

如果不做出上面的转换,那么 ptr 指向的并不是 derived 的 base2 subobject
。后果是,ptr 将一个derived类型当做base2类型来用。

当要delete ptr时又面临了一次转换,因为在delete ptr的时候,需要对整个对象而不是其子对象施行delete运算符,这期间需要调整ptr指向完整的对象起点,因为不论是调用正确的析构函数还是delete运算符都需要一个指向对象起点的指针,想一想给予一个derived类的成员函数指向base2 subobjuect 的this指针会发生什么吧。因为ptr的具体类型并不知道,所以必须要等到执行期来完成

有一种叫做thunk的方法,thunk的作用在于:

  1. 以适当的offset值来this调整指针.
  2. 跳到虚函数中去。

Thunk技术即是:虚函数表中的slot仍然继续放一个虚函数实体地址,但是如果调用这个虚函数需要进行this调整的话,该slot中的地址就指向一个Thunk而不是一个虚函数实体的地址。

书中纷杂的讲到不少中种情况,但我以我的理解,做如下小结:

多继承下的虚函数,影响到虚函数的调用的实质上为this的调整。而this调整一般为两种

  1. 调整指针指向对应的subobject,一般发生在继承类类型指针向基类类型指针赋值的情况下。
  2. 将指向subobject的指针调整回继承类对象的起始点,一般发生在基类指针对继承类虚函数进行调用的时候。

第一点,使得该基类指针指向一个与其指针类型匹配的子对象,唯有如此才能保证使得该指针在执行与其指针类型相匹配的特定行为的正确性。比方调用基类的成员,获得正确的虚函数地址。可以想象如果不调整,用ptr存取base2 subobject的数据成员时,会发生什么?调用base2的成员函数的时候,其成员函数接受的this指针指向derived
类型对象,这又会发生什么?结果是整个对象的内存结构有可能都被破坏。还有别忘了,vptr也可以看做一个数据成员,要找到虚函数,前提是获取正确的vptr偏移量。

而第二点,显然是让一个继承类的虚函数获取一个正确的this指针,因为一个继承类虚函数要的是一个指向继承类对象的this指针,而不是指向其子对象。

第一顺序继承类之所以不需要进行调整的关键在于,其subobject的起点与继承类对象的起点一致。

(我自己画了一个图:


虚拟继承下的虚函数

Lippman说,如果一个虚基类派生自另一虚基类,而且它们都支持虚函数和非静态数据成员的时候,编译器对虚基类的支持就像迷宫一样复杂。其实我原想告诉他,我是怀着一颗勇士之心而来的你说呢?。

虽然书中没有介绍太多,但不难猜测的是在虚继承情况下,复杂点在仍旧在于this指针的调整,然而其复杂度显然又在多继承之上,因为又多了一个vbptr了。

原文链接:(作者写的很好)http://www.roading.org/develop/cpp/c之虚函数virtual-member-functions.html

C++——虚函数(Virtual Member Functions) 【functions语意学】相关推荐

  1. 探讨 C++ 虚函数 virtual

    探讨 C++ 虚函数 virtual 探讨 C++ 虚函数 virtual 有无虚函数的对比 虚函数表 验证虚函数表的存在性 探讨 C++ 虚函数 virtual 有无虚函数的对比 C++ 中的虚函数 ...

  2. C#中的虚函数virtual

    简单介绍虚函数virtual 在某基类中声明 virtual 并在一个或多个派生类中被重新定义的成员函数称为虚函数. 虚函数的作用就是实现多态性(Polymorphism),多态性是将接口与实现进行分 ...

  3. C++ 第三章 纯虚函数Virtual报错

    Virtual 后的函数=0则为纯虚函数 #include <iostream>using namespace std;class FA {public:void Func1() {pri ...

  4. 为什么虚函数(virtual)不能是static函数

    简而言之,成员函数实例相关,静态函数类相关. 虚函数,是一种特殊的成员函数,用来实现运行时多态. 静态成员函数,可以不通过对象来调用,没有隐藏的this指针. virtual函数一定要通过对象来调用, ...

  5. c 语言的虚函数,C 中的虚函数(virtual function)

    一.简介 虚函数是C++中用于实现多态(polymorphism)的机制.核心理念就是通过基类访问派生类定义的函数.假设我们有下面的类层次: class Father { public: virtua ...

  6. C++纯虚函数 virtual =0

    2019独角兽企业重金招聘Python工程师标准>>> 纯虚函数(pure virtual function) class Screen { public:virtual void ...

  7. C++中的虚函数(virtual function)

    1回顶部 一.简介 虚函数是C++中用于实现多态(polymorphism)的机制.核心理念就是通过基类访问派生类定义的函数.假设我们有下面的类层次: class A { public: virtua ...

  8. C++中重写与覆写(虚函数virtual)的区别

    本文章已收录于:                                                       虚函数的情况下调用成员函数时调用的是指向对象的所属类的成员函数例子中为ap ...

  9. C++ 学习之旅(16)——虚函数与纯虚函数virtual

    关于虚函数的概念讲解,可以看这篇文章: https://blog.csdn.net/siwuxie095/article/details/71159414 以下用例子进行实际说明: #include ...

最新文章

  1. sap事务代码_SAP事务码太多,记不住怎么办?
  2. 网站维护工作要专业更要谨慎
  3. KlayGE中的FXAA已经完成
  4. TCP协议可靠性保证(确认应答机制,超时重传机制,流量控制,拥塞窗口)
  5. PgSQL · 最佳实践 · CPU满问题处理
  6. IDEA远程调试服务器代码
  7. leetcode98. 验证二叉搜索树
  8. C++实现AOE网中的关键路径算法(邻接表存储)
  9. Python模块及其导入
  10. JEECMS编辑漏洞及随便拿webshell,啊哈哈
  11. 使用Nacos搭建微服务注册中心和配置中心(二)
  12. C++的类型强制转换,static_cast,dynamic_cast,const_cast,reinterpret_cast
  13. 洛谷 P2936 [USACO09JAN]全流Total Flow
  14. Odoo 去掉 恼人的 上午和下午
  15. 详解Vue中的自定义指令
  16. CentOS安装jre环境
  17. WebService技术入门
  18. NASA 用哈勃望远镜定格你的星空
  19. 免费图片文字识别,非常好用
  20. 正确的座机号码格式_电话号码格式怎么输入才是正确

热门文章

  1. hadoop 集群开启之后datanode没有启动问题
  2. asp.net core 创建允许跨域请求的api, cors.
  3. bzoj 2375: 疯狂的涂色
  4. python Counter类
  5. keras中的fit函数参数_keras的fit_generator与callback函数
  6. java 主动抛出 段错误_段错误产生的原因~
  7. Linux ls信息给qt gui,如何使用Qt 4把ls命令的结果显示到GUI界面上去?
  8. mupdf不支持x64_ARM版Win10用户狂喜 微软全新补丁让应用不再不兼容
  9. MySQL的共享锁和独占锁
  10. 跳过51单片机,直接学STM32有什么严重后果?