• [C/C++] 虚函数机制


    转自:c++ 虚函数的实现机制:笔记

    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)、按照函数名称查表,这种方案可以避免如上的问题;但是由于要比较名称,有时候要遍历所有的继承结构,时间效率性能不是很高

    3、总结:

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

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

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

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

  • 相关阅读:
    Windows服务器上Mysql为设置允许远程连接提示:not allowed to connect to this MySQL server
    Java中怎样遍历两个Date日期之间的每一天
    ElementUI中el-table设置指定列固定不动,不受滚动条影响
    APP测试时不可忽视搭建代理服务器抓包测试的必要性
    ctype库试运行
    django-grappelli 安装配置
    windows下django1.7 +python3.4.2搭建记录2
    windows下django1.7 +python3.4.2搭建记录1
    【2020-11-15】大心境并不能自动化解小结
    【2020-11-14】对自己责任心的煎熬考验
  • 原文地址:https://www.cnblogs.com/lca1826/p/6791835.html
Copyright © 2020-2023  润新知