1、编译器必须实现出C++语言的特性。一般情况下,我们只需要使用这些特性就好了,不需要关心内部的实现细节。但是,有些特性的实现,会对对象的大小和成员方法的执行速度造成影响。因此,有必要了解内部实现的细节。

2、首先考虑虚方法,虚方法是用来实现多态的。多态是指对于指针和引用,表面类型和真实类型不一致的情况下,调用真实类型的虚方法。

3、虚方法有关的实现细节为:

  a、父类有一个虚方法表(vtbl),可以认为是一个方法指针的数组(这里注意:对于数组,我们知道元素的类型必须一致,虚方法表中的虚方法类型是不一样的,这里进行了特殊处理),方法指针指向父类的虚方法。

  b、子类整体拷贝父类的虚方法表,对于重写的虚方法,在相同位置置换为重写后的虚方法地址,对于新增的虚方法,在数组的尾部添加。

  c、对于多态类的对象,内部有一个字段为vptr,指向该类的vtbl。考虑,构造子类对象,首先调用父类构造方法,将vptr初始化为指向父类的虚方法表,然后调用子类的构造方法,将vptr重置为指向子类的虚方法表。

4、需要注意的情况:

  a、虚方法表是对应于类的,一个类有一个虚方法表。一般情况下,内存的消耗可以忽略。但是考虑极端的情况,一个父类有1000个虚方法,子类重写一个虚方法,并且有大量类似的子类,出现相同的方法指针,存储多次,就会导致占用大量的内存。

  b、对象多一个vptr字段,如果对象本身比较小,vptr占用的内存比例就大了。

  c、C++重写为什么要使用virtual关键字?从封装角度而言,类本身是个命名空间,有一个范围的概念。父类是大范围,子类是小范围,在C++中,小范围的名称会隐藏大范围的名称,而不关心名称的类型。使用virtual,其实是告诉编译器不要进行隐藏。把该方法保存到虚方法表中(可以认为是一种特殊情况的隐藏)。

  d、在编译的时候,编译器只知道指针或者引用的表面类型。不同类型的指针,本质上没有区别,就是一个地址。重要的是,可以告诉编译器按照什么样的方式去解释指向的内存。这就引出一个问题,把子类对象当成父类对象来解释,不会出现问题。如何保证呢?

    第一点,子类对象和父类对象在相同位置都有一个vptr,一般在头部。

    第二点,子类虚方法表和父类虚方法表在位置上是一一对应的。

    比如:pc->f1(); 产生的代码是:(* pc->vptr[i]) (pc); 找到第i个虚方法指针,解引用,pc传递为this指针。

  e、一般情况下,重写要求:子类方法与父类方法,形参表和返回类型完全一致。但是有两种特殊情况:

    重写的析构方法,子类父类的方法名各自为本身类名;

    父类返回Base*,子类可以返回Derived*,目前C++支持部分的逆变协变,还不支持完全的逆变协变。

  f、虚方法不能inlined,这个很好理解。inline可认为编译时文本替换,虚方法运行时确定方法的调用,二者矛盾。

5、多重继承,使问题更复杂。每个对象含有多个vptr,针对不同的父类vtbl,子类产生一个特殊的vtbl。

6、考虑,D->B->A,D->C->A,会导致A的字段在D中有两份,这显然不合理。为了解决这个问题,使用虚拟继承。B,C虚继承A。

7、考虑RTTI,C++提供关键字typeid 获取类的type_info对象。一个类对应于一个type_info对象,类及其所有的对象共享。如 int a, Person p;

  typeid(a) 转化为 typeid(int) 求值;

  typeid(p) 转化为 typeid(Person) 求值;

8、如果不是多态类,也就是没有虚方法,typeid(*base) 返回表面类型。如果是多态类,typeid(*base) 可以返回真实类型。这意味着内部有一定的实现方法。可以认为,在类的虚方法表中第一项就是当前类的type_info属性。这也解释了,为什么只有多态类才能用typeid求出真实类型。非多态类没有虚方法表。

【M24】了解虚方法、多继承、虚基类、RTTI的成本的更多相关文章

  1. java 虚方法。 后面new 那个类, 就调用哪个类的方法 ,而非定义类的方案。 关于父子 类的 呵呵

    java   虚方法.     后面new  那个类, 就调用哪个类的方法 ,而非定义类的方案.  关于父子 类的   呵呵 在多态的情况下,声明为父类类型的引用变量只能调用父类中的方法,但如果此变量 ...

  2. C++//菱形继承 //俩个派生类继承同一个基类 //又有某个类同时继承俩个派生类 //成为 菱形继承 或者 钻石 继承//+解决

    1 //菱形继承 2 //俩个派生类继承同一个基类 3 //又有某个类同时继承俩个派生类 4 //成为 菱形继承 或者 钻石 继承 5 6 #include <iostream> 7 #i ...

  3. C++ | 继承(基类,父类,超类),(派生类,子类)

    转载:https://blog.csdn.net/Sherlock_Homles/article/details/82927515 文章参考:https://blog.csdn.net/war1111 ...

  4. 访问祖先类的虚方法(直接访问祖先类的VMT,但是这种方法在新版本中未必可靠)

    访问祖先类的虚方法 问题提出 在子类覆盖的虚方法中,可以用inherited调用父类的实现,但有时候我们并不需要父类的实现,而是想跃过父类直接调用祖先类的方法. 举个例子,假设有三个类,实现如下: t ...

  5. servlet、filter、listener继承的基类和获得作用域的方式

    一.servlet: 1.servlet属于j2ee的组件,构建servlet的web project不需要导入项目框架jar包 2.servlet的体系结构:  在j2ee API中,提供给serv ...

  6. 修改tt模板让ADO.NET C# POCO Entity Generator With WCF Support 生成的实体类继承自定义基类

    折腾几天记载一下,由于项目实际需要,从edmx生成的实体类能自动继承自定义的基类,这个基类不是从edmx文件中添加的Entityobject. 利用ADO.NET C# POCO Entity Gen ...

  7. C# 类中的静态字段始终继承自基类

    我们试想一下现在有一个类Parent,它有一个static的int类型字段number,然后如果类Parent有三个子类Child01.Child02和Child03,那么改变Parent.numbe ...

  8. C#抽象类、抽象方法、虚方法

    定义抽象类和抽象方法: abstract 抽象类特点: 1.不能初始化的类被叫做抽象类,它们只提供部分实现,但是另一个类可以继承它并且能创建它们的实例 2.一个抽象类可以包含抽象和非抽象方法,当一个类 ...

  9. 浅谈C#抽象方法、虚方法、接口

    每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不 ...

  10. C#中virtual(虚方法)的理解以及和abstract(抽象方法)的区别

    Virtual方法(虚方法) virtual 关键字用于在基类中修饰方法.virtual的使用会有两种情况: 情况1:在基类中定义了virtual方法,但在派生类中没有重写该虚方法.那么在对派生类实例 ...

随机推荐

  1. Ajax轮询以及Comet模式—写在Servlet 3.0发布之前(转)

    2008 年的夏天,偶然在网上闲逛的时候发现了 Comet 技术,人云亦云间,姑且认为它是由 Dojo 的 Alex Russell 在 2006 年提出.在阅读了大量的资料后,萌发出写篇 blog ...

  2. Drupal 7.23:函数drupal_alter()注释

    /** * Passes alterable variables to specific hook_TYPE_alter() implementations. * * This dispatch fu ...

  3. 第二个UI脚本--Python+selenium之unittest+HTMLtestRunner及python的继承

    前面有一篇对于常见元素的识别和操作的python自动化脚本,这一篇就接着聊下python的类继承,已经它的第三款unittest框架,和报告收集包HTMLtestRunner的应用. 还是直接上代码吧 ...

  4. 《Python 学习手册4th》 第十九章 函数的高级话题

    ''' 时间: 9月5日 - 9月30日 要求: 1. 书本内容总结归纳,整理在博客园笔记上传 2. 完成所有课后习题 注:“#” 后加的是备注内容 (每天看42页内容,可以保证月底看完此书) “重点 ...

  5. LR之录制SQL脚本

    1.选择协议 ①MS SQL serve ②ODBC 一般情况下选ODBC 2.录制步骤

  6. 两款较好的Web前端性能测试工具

    前段时间接手了一个 web 前端性能优化的任务,一时间不知道从什么地方入手,查了不少资料,发现其实还是蛮简单的,简单来说说. 一.前端性能测试是什么 前端性能测试对象主要包括: HTML.CSS.JS ...

  7. 【转】GUID学习

    概念 GUID: 即Globally Unique Identifier(全球唯一标识符) 也称作 UUID(Universally Unique IDentifier) . GUID是一个通过特定算 ...

  8. Tcl之Intro

    Tool command language, a widely used scripting tool that was deveoped for controlling and extending ...

  9. Android4.4 耳机检测分析

    在ALSA架构中,ASOC是由3个部分组成:Platform.CODEC & Machine.而耳机检测一般是在Machine driver里实现,当然也可以在CODEC driver里实现. ...

  10. Array.prototype.slice.call

    Array.prototype.slice.call(arguments)能将具有length属性的对象转成数组 ,::'age'}; Array.prototype.slice.call(arr); ...