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. DDoS攻防战(一):概述

    原文出处: 陶辉的博客   欢迎分享原创到伯乐头条 (此图摘自<Web脚本攻击与防御技术核心剖析>一书,作者:郝永清先生) DDoS,即 Distributed Denial of Ser ...

  2. 继承View绘制正方形且360旋转

    import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; impor ...

  3. Android中设置全屏的方法

    在实际的应用程序开发中,我们有时需要把 Activity 设置成全屏显示,一般情况下,可以通过两种方式来设置全屏显示效果.其一,通过在代码中可以设置,其二,通过manifest配置文件来设置全屏. 其 ...

  4. java多线程之synchronized(线程同步)

    一.线程同步,主要应用synchronized关键字: public class TraditionalThreadSynchorinzed { public static void main(Str ...

  5. javascript常用的小知识

    1. oncontextmenu="window.event.returnvalue=false" 将彻底屏蔽鼠标右键 <table border oncontextmenu ...

  6. 第三百三十八天 how can I 坚持

    下午去奥体森林公园跑了个步,好累. 晚上和徐斌同学一起吃了个饭,接触了下杨辉三角,还没整明白,明天看下. 突然想起java 编译,ant脚本. 要精通门技术,还要赶上潮流. 睡觉.

  7. Spark RDD概念学习系列之为什么会引入RDD?(一)

        为什么会引入RDD? 我们知道,无论是工业界还是学术界,都已经广泛使用高级集群编程模型来处理日益增长的数据,如MapReduce和Dryad.这些系统将分布式编程简化为自动提供位置感知性调度. ...

  8. Netbeans Platform 工程,免安装JDK

    使用Netbeans 6.8 创建了一个Netbeans Platform 工程,以Zip形式发布后, 按照以下操作,可 以在客户端免安装JDK: 1. 从已安装JDK的计算机中,提取JDK:eg. ...

  9. Collection Operators

    [Collection Operators] Collection operators are specialized key paths that are passed as the paramet ...

  10. Options for Debugging Your Program or GCC

    [Options for Debugging Your Program or GCC] -g Produce debugging information in the operating system ...