zoukankan      html  css  js  c++  java
  • 【C++】虚函数的实现机制

    一.什么是虚函数

    虚函数是在类中由virtual关键字声明的成员函数,并且每一个含有虚函数的类都至少有一个与之对应的虚函数表,其中存放着该类所有虚函数对应的函数指针

    在基类中进行如下定义:

    virtual void show() //由于有virtual修饰所以是虚函数

    void show()//虽然和前面声明的show虚函数同名,但不是虚函数

    虚函数表

    • B的虚函数表中存放着B::foo和B::bar两个函数指针。
    • D的虚函数表中存放的既有继承自B的虚函数B::foo,又有重写(override)了基类虚函数B::bar的D::bar,还有新增的虚函数D::quz。

    所有虚函数地址都会存放在所属类的虚函数表vtbl中,另外在基类中声明为虚函数的成员方法,达到子类是仍然是虚函数,即使子类中重新定义基类虚函数时未使用virtual修饰,该函数地址仍会放在子类的虚函数表vtbl中


    二.虚函数表是如何构造和继承的?

    1)基类虚函数表的构造:

    首先在基类声明中找到所有虚函数,按照其声明顺序编码,然后按照此声明顺序为基类创建一个虚函数表,其内容就是指向这些虚函数的函数指针,按照虚函数声明的顺序将这些虚函数的地址填入虚函数表中,例如若show放在虚函数声明的第二位,则在虚函数表中也放第二位

    2)子类虚函数表的构建和继承:

    首先将基类的虚函数表复制到该子类的虚函数表指针中,若子类重写了基类的虚函数show,则将子类的虚函数表存放show的函数地址更新未重写后函数的函数指针(未重写前存放的是子类的show虚函数的函数地址),若子类增加了一些虚函数的声明,则将这些虚函数的地址加到该类虚函数表的后面

    虚函数表的构造过程


    三.虚函数的调用过程/虚函数表是如何访问的?

                  基类     Base::virtualvoid show();              (1)

                  子类     Extend::virtualvoid show();             (2)

                       Extern ext;

                       Base*pBase=&ext;

                       pBase->show();

    当指向pBase->show()时,要观察show在Base基类中声明的是虚函数还是非虚函数,若为虚函数将使用动态联编(使用虚函数表决定如何调用函数),若为非虚函数则使用静态联编(根据调用指针pBase的类型来确定调用哪个类的成员函数),此处show为虚函数,首先由于检查到PBase指针类型所指的类Base中show定义为虚函数,因此找到pBase所指对象(有可能是Base类型也有可能是Extern类型),访问对象得到该对象的所属类虚函数地址表,其次,查找show在Base类中声明的位置以此找到其在show在Base类中所有虚函数声明中的位序,然后到pBase所指对象的所属类的虚函数表中访问该位序的函数指针,从而得到要执行的函数

    当执行pBase->show();时首先到Base中查看show(),发现其为虚函数,然后访问pBase指向的ext对象,在对象中得到Extend类的虚函数表,在Base类声明中找到show()声明的位序0,访问Extend类的虚函数表的位置0,得到show的函数地址。注意若只有基类定义了virtual void show();而子类未重写virtual void show();即上面的函数(2),则Extend虚函数表中的位序0中存放的地址仍然是Base类中定义的virtual void show()函数,而若Extend类中重写了Base类中的virtual void show()方法,则Extend的虚函数表中位序0的函数地址将被更新为Extend中新重写的函数地址。从而调用pBase->show()时将产生多态的现象。

    总结:当调用pBase->show();时,执行的步骤:

    1.判断Base类中show是否为虚函数。

    2.若不是虚函数则找到pBase所指向的对象所属类Base。执行Base::show()。若是虚函数则执行步骤3.

    3.访问pBase所指对象的虚函数表指针得到pBase所指对象所在类的虚函数表。

    4.查找Base中show()在声明时的位序为x,到步骤3得到的虚函数表中找到位序x,从而得到要执行的show的函数地址。

    5.根据函数地址和Base中声明的show的函数类型(形参和返回值)访问地址所指向的函数。

    无论pBase指向哪种类型的对象,只要能够确定被调函数在虚函数中的偏移值,待运行时,能够确定具体类型,并能找到相应vptr了,就能找出真正应该调用的函数。


  • 相关阅读:
    素数路径Prime Path POJ3126 素数,BFS
    Fliptile POJ3279 DFS
    Find the Multiple POJ1426
    洗牌Shuffle'm Up POJ3087 模拟
    棋盘问题 POJ1321 DFS
    抓住那只牛!Catch That Cow POJ3278 BFS
    Dungeon Master POJ2251 三维BFS
    Splitting into digits CodeForce#1104A
    Ubuntu下手动安装Nvidia显卡驱动
    最大连续子序列和
  • 原文地址:https://www.cnblogs.com/yinbiao/p/11603618.html
Copyright © 2011-2022 走看看