转自:http://blog.csdn.net/jiangnanyouzi/article/details/3720807

1、c++实现多态的方法

其实很多人都知道,虚函数在c++中的实现机制就是用虚表和虚指针,但是具体是怎样的呢?从more effecive c++其中一篇文章里面可以知道:是每个类用了一个虚表,每个类的对象用了一个虚指针。具体的用法如下:

class A
{
public:
    virtual void f();
    virtual void g();
private:
    int a
};

class B : public A
{
public:
    void g();
private:
    int b;
};

//A,B的实现省略

因为A有virtual void f(),和g(),所以编译器为A类准备了一个虚表vtableA,内容如下:

A::f 的地址
A::g 的地址

B因为继承了A,所以编译器也为B准备了一个虚表vtableB,内容如下:

A::f 的地址
B::g 的地址

注意:因为B::g是重写了的,所以B的虚表的g放的是B::g的入口地址,但是f是从上面的A继承下来的,所以f的地址是A::f的入口地址。

然后某处有语句 B bB;的时候,编译器分配空间时,除了A的int a,B的成员int b;以外,还分配了一个虚指针vptr,指向B的虚表vtableB,bB的布局如下:

vptr : 指向B的虚表vtableB
int a: 继承A的成员
int b: B成员

当如下语句的时候:
A *pa = &bB;

pa的结构就是A的布局(就是说用pa只能访问的到bB对象的前两项,访问不到第三项int b)

那么pa->g()中,编译器知道的是,g是一个声明为virtual的成员函数,而且其入口地址放在表格(无论是vtalbeA表还是vtalbeB表)的第2项,那么编译器编译这条语句的时候就如是转换:call *(pa->vptr)[1](C语言的数组索引从0开始哈~)。

这一项放的是B::g()的入口地址,则就实现了多态。(注意bB的vptr指向的是B的虚表vtableB)

另外要注意的是,如上的实现并不是唯一的,C++标准只要求用这种机制实现多态,至于虚指针vptr到底放在一个对象布局的哪里,标准没有要求,每个编译器自己决定。我以上的结果是根据g++ 4.3.4经过反汇编分析出来的。

2、两种多态实现机制及其优缺点

除了c++的这种多态的实现机制之外,还有另外一种实现机制,也是查表,不过是按名称查表,是smalltalk等语言的实现机制。这两种方法的优缺点如下:

(1)、按照绝对位置查表,这种方法由于编译阶段已经做好了索引和表项(如上面的call *(pa->vptr[1]) ),所以运行速度比较快;缺点是:当A的virtual成员比较多(比如1000个),而B重写的成员比较少(比如2个),这种时候,B的vtableB的剩下的998个表项都是放A中的virtual成员函数的指针,如果这个派生体系比较大的时候,就浪费了很多的空间。

比如:GUI库,以MFC库为例,MFC有很多类,都是一个继承体系;而且很多时候每个类只是1,2个成员函数需要在派生类重写,如果用C++的虚函数机制,每个类有一个虚表,每个表里面有大量的重复,就会造成空间利用率不高。于是MFC的消息映射机制不用虚函数,而用第二种方法来实现多态,那就是:

(2)、按照函数名称查表,这种方案可以避免如上的问题;但是由于要比较名称,有时候要遍历所有的继承结构,时间效率性能不是很高。(关于MFC的消息映射的实现,看下一篇文章)

3、总结:

如果继承体系的基类的virtual成员不多,而且在派生类要重写的部分占了其中的大多数时候,用C++的虚函数机制是比较好的;

但是如果继承体系的基类的virtual成员很多,或者是继承体系比较庞大的时候,而且派生类中需要重写的部分比较少,那就用名称查找表,这样效率会高一些,很多的GUI库都是这样的,比如MFC,QT

PS. 其实,自从计算机出现之后,时间和空间就成了永恒的主题,因为两者在98%的情况下都无法协调,此长彼消;这个就是计算机科学中的根本瓶颈之所在。软件科学和算法的发展,就看能不能突破这对时空权衡了。呵呵

何止计算机科学如此,整个宇宙又何尝不是如此呢?最基本的宇宙之谜,还是时间和空间~

C++的虚函数使用了一个虚函数表来存放了每一个虚函数的入口地址,这个虚函数表又使用一个虚函数指针来进行访问。通常,虚函数指针都放在对象模型的第一个位置存放,这样访问徐函数指针的速度最快,没有偏移量。通过虚函数指针,找到虚函数表,进而再做一个次偏移量计算,来得到真实虚函数的入口地址,从而访问了虚函数。这样看来,访问一个虚函数将使用两次间接计算,故要失去一些时间效率。另外,使用了虚函数,那么就要消耗一些空间,存放虚函数指针。但是,这都是值得的,为了实现多态。

c++实现多态的方法 虚表相关推荐

  1. 学习-Java继承和多态之方法重载

    第1关:学习-Java继承和多态之方法重载 任务描述 相关知识 编程要求 测试说明 任务描述 本关任务:使用重载方法为 Student 类创建三个构造方法. 相关知识 Java 中重载是指一个类中可以 ...

  2. 什么是继承,什么是多态,方法的重载和覆盖有何区别?

    什么是继承,什么是多态,方法的重载和覆盖有何区别?   1.什么是继承?   继承是子类调用父类的属性和方法.在Java中通过extends关键字实现,格式为Class 子类 extends 父类{主 ...

  3. java基础2:this、static、final、abstract关键字、代码块、生成API工具包、继承、多态、方法重载和重写,抽象类、接口、常见异常、java权限修饰符、选择排序思想和程序和内部类

    1.this关键字 this表示当前类对象的地址空间值的引用.就是说,this代表了new出来的对象 其最大作用就是解决了构造器中局部变量隐藏成员变量.即用this.变量名 = 变量名 区分. 2.s ...

  4. 类与接口(五)java多态、方法重写、隐藏

    一.Java多态性 面向对象的三大特性:封装.继承.多态. 多态的类型,分为以下两种: 编译时多态: 指的是 方法重载.编译时多态是在编译时确定调用处选择那个重载方法,所以也叫 静态多态,算不上真正的 ...

  5. Java 面向对象编程(三)——多态,方法重写,向上转型,向下转型

    所谓多态,就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪 ...

  6. 【多态】【虚表指针与虚表】【多继承中的多态】

    前言

  7. (JAVA学习笔记) 类的继承,super,方法的重写,多态

    文章目录 类的继承,super,方法的重写,多态 1.类的继承 代码演示: 2.super和this 代码演示: 3.方法的重写 代码演示: 4.多态 代码演示: 类的继承,super,方法的重写,多 ...

  8. Python面向对象,类,继承,多态及鸭子类型,获取类的类型,方法和属性(类似java的反射)

    1.创建类的代码: #在Java中,所有的最终父类都是Object #在python中也是,一个类没有直接的父类就写入objectclass Animal(object):#在python中init方 ...

  9. python3:面向对象(多态和继承、方法重载及模块)

    1.多态 同一个方法在不同的类中最终呈现出不同的效果,即为多态. class Triangle:def __init__(self,width,height):self.width = widthse ...

最新文章

  1. selenium:学习资源
  2. python 中cookie_详解Python中的Cookie模块使用
  3. 消息队列的实践php,php消息队列处理实践 ,利用AMQP和redis两种方法
  4. java系统系统异常处理,银行系统(Java)异常处理
  5. java基本语法心得_Java学习笔记(一)——基础语法(上)
  6. Chrome 73 DevTools 新功能之 Logpoints
  7. 电子密封胶正常固化与非正常固化差别是什么?
  8. 高级IO(一)--UNIX环境高级编程读书笔记
  9. 剑指offer——面试题35:第一个只出现一次的字符
  10. 数据科学高级分析 (Data science advanced analytics)
  11. linux内核升级到3.4
  12. java获取元素创建时间_Golang中使用Date进行日期格式化(沿用Java风格)
  13. Fortran 注释符号
  14. 基于Web的svg编辑器(1)——撤销重做功能
  15. 电脑重装系统引导方式不是BIOS 不能引导MBR磁盘怎么办
  16. 为MacTex配置Ctex环境
  17. 哈罗数据分析(SQL)笔试
  18. yolov5s-5.0网络模型结构图
  19. FreeNOS-kernel目录下boot.S、链接脚本kernel.ld文件分析(四)
  20. ShowMeAI —— Show u 三连

热门文章

  1. 五十八、Vue中的计算属性,方法和侦听器
  2. “玩转标签,发现层次的力量!”:跨模态哈希方法研究
  3. HDU2091 空心三角形 水题
  4. linux查看ibmmq进程,ibm-mq – 如何检查IBM MQ for Linux中是否存在队列名称?
  5. 单例模式volatile
  6. Spring Security——自定义认证错误提示信息及自适应返回格式解决方案
  7. TensorFlow 2——【module ‘tensorflow.compat.v1‘ has no attribute ‘contrib‘】解决方案
  8. IDM——服务器响应显示您没有权限下载此文件(百度网盘下载问题)
  9. Problem for Nazar
  10. 【mybatis学习记录】mybatis的各种查询 一对一关联查询(4种方式) 一对多(2种方式)