• 我理解的C++虚函数表


    今天拜读了陈皓的C++ 虚函数表解析的文章,感觉对C++的继承和多态又有了点认识,这里写下自己的理解。如果哪里不对的,欢迎指正。如果对于C++虚函数表还没了解的话,请先拜读下陈皓的C++ 虚函数表解析的文章,不然我写的可能你看不懂。

    以前一直对于c++多态感觉很神奇,从书上看,多态就是在构造子类对象的时候,通过虚函数,利用父类指针,来调用子类真正的函数。这个解释是正确的,但是它是怎么实现的呢,一直再猜想。以前也知道有虚函数表这件事,也没有仔细理解是什么东东。今天仔细读了陈皓的文章,才明白C++多态的原理。这里说说我的理解:

     先附上我写的一段简单代码:

      1 #include <iostream>
      2 using namespace std;
      3 
      4 class Base{
      5 public:
      6     virtual void f() { cout << "Base::f()" << endl;}
      7     virtual void g() { cout << "Base::g()" << endl;}
      8 };
      9 class Derive : public Base {
     10 public:
     11     virtual void f() { cout << "Devive::f()" << endl;}
     12     virtual void f1() { cout << "Devive::f1()" << endl;}
     13     virtual void g1() { cout << "Devive::g1()" << endl;}
     14 };
     15 typedef void (*Fun)(void);
     16 
     17 int main()
     18 {
     19     Fun pFun = NULL;
     20 
     21     Base b;
     22     cout << "virtual table address: " << (int*)(&b) << endl;
     23     cout << "first virtual function address: " << (int*)*(int*)(&b) << endl;
     24     pFun = (Fun)(*(int*)*(int*)(&b));
     25     pFun();
     26     pFun = (Fun)*((int*)*(int*)(&b)+1);
     27     pFun();
     28 
     29     cout << "*********" << endl;
     30 
     31     Derive d;
     32     cout << "virtual table address: " << (int*)(&d) << endl;
     33     cout << "first virtual function address: " << (int*)*(int*)(&d) << endl;
     34     pFun = (Fun)(*(int*)*(int*)(&d));
     35     pFun();
     36     pFun = (Fun)*((int*)*(int*)(&d)+1);
     37     pFun();
     38     pFun = (Fun)*((int*)*(int*)(&d)+2);
     39     pFun();
     40     pFun = (Fun)*((int*)*(int*)(&d)+3);
     41     pFun();
     42 

     输出结果:

    virtual table address: 0xbfd268f8
    first virtual function address: 0x8048b90
    Base::f()
    Base::g()
    *********
    virtual table address: 0xbfd268f4
    first virtual function address: 0x8048b78
    Devive::f()
    Base::g()
    Devive::f1()
    Devive::g1()

    理解1:首先你想要实现多态就必须通过虚函数,如果第6行我们改为 void f() { cout << "f" << endl;}, 那么顾名思义这个f()函 数就不会进入虚函数表中,也就没有多态之说了

     

    理解2:在32位系统和64位系统下,取得虚函数表里的函数的方法还有所不同,再32位系统下,如程序那样取就行,而在64位系统下,取得第二个函数(Fun)*((int*)*(int*)(&d+2),第三个函数(Fun)*((int*)*(int*)(&d+4)....

    理解3:对于程序清单里,我们分别输出类 Base和类Derive的v-table的地址和第一个虚函数的地址,我们发现,他们地址根本不一样,所以说,C++会为每一个类都分配一个virtual table

    理解4:如果我们分别打印 *(int*)*(int*)(&b),*(int*)*(int*)(&d),我们发现他们的值都是一样的,这就验证我的猜想,虚函数表存放都是虚函数的指针(这话说的有点蛋疼,但这不是关键),故子类调用父类的时候,调用函数,是去查的虚函数表,然后查到函数的真正的位置,然后调用

    理解5:在理解了虚函数表的结构后,我觉得多态的函数调用,是跟子类的虚函数表有关的,可以说是跟子类没有直接关系的(我以前觉得多态函数的调用是先去父类的函数中找这个函数,然后在这个子类中找同样的函数调用它,现在想来真是2到渣的节奏)

    附几张图,来说明这个调用关系

    说明一下,父类a的函数分别是virtual void f(),virtual void g(),virtual void h()

         子类b的函数分别是virtual void f1(), virtual void g1(), virtual void h1()

         子类c的函数分别是virtual void f(), virtual void g1(), virtual void h1()

    类a.只有父类时

     

    类b.有子类继承,但是没有函数的重载

     

    类c.有子类继承,有函数重载

     

    如:我们调用

    Derive c;

    Base *p_base = &c;

    p_base->f();

    这里的调用过程是,在类C虚函数表中查找到Base类的f()的地址【虚函数表内函数的存放顺序是,先放父类的虚函数,然后再存放子类的虚函数】,然后在该地址处取得存放的真正的函的地址,即是子类的函数,故调用的是子类的函数

     版权所有,如要转载请说明出处及作者

  • 相关阅读:
    php中如何实现在线网友
    用php与mysql的电子贺卡代码
    基于PHP MySQL的聊天室设计
    xml php动态载入与分页
    模拟OICQ的实现思路和核心程序
    FC4下安装plog快速指南(plog版本:1.01)
    一个简单的php在线端口扫描器
    UVA 10604 Chemical Reaction(六维dp数组)
    HDU 1503 Advanced Fruits
    hust 1607 Triangles(经典好题)
  • 原文地址:https://www.cnblogs.com/457220157-FTD/p/4011214.html
Copyright © 2020-2023  润新知