• C++ 链式继承下的虚函数列表


    目录

    1.虚函数列表的位置

    2.虚函数列表的内容

    3.链式继承中虚函数列表的内容

    注:

    虚函数列表 又称为虚表, vtbl , 指向它的指针称为vptr, vs2019中称为__vfptr

    操作系统: windows 10 专业版 64位

    编译器: Visual Studio 2019 Community

    1.虚函数列表的位置

    结论

    编译器一般会保证指向虚函数列表的指针存在于对象实例中最前面的位置

    而虚函数列表中的内容, 就是多个函数指针

    代码验证:

    首先声明一个基类Base和一个派生类Derived

    class  Base
    {
    public:
      virtual void f() { std::cout << "Base1::f" << std::endl; }
      virtual void g() { std::cout << "Base1::g" << std::endl; }
      virtual void h() { std::cout << "Base1::h" << std::endl; }
      virtual void i() { std::cout << "Base1::i" << std::endl; }
    };
    
    class Derived : public Base
    {
      virtual void g() override { std::cout << "Derived::g" << std::endl; }
      virtual void h1() { std::cout << "Derived::i1" << std::endl; }
    };
    

    然后实例化一个派生类的对象

    Derived derived;
    

    现在我们打印出该对象的地址

    std::cout << "derived对象的地址: " << (&derived) << std::endl;
    

    由于我们假定指向虚函数列表的<指针>存在于对象实例中最前面的位置

    那么我们可以认定, derived对象的地址中的开头是一个指针的地址(称之为指针pA)

    而这个指针(pA)指向虚函数列表中的开头, 也就是一个函数指针(称之为指针pF)

    所以这个指针(pA), 是一个指向指针的指针, 即指向指针(pF)的指针(pA)

    基于这个推测, 我们将derived对象的地址指针pA的地址进行一个类型转换

    使用reinterpret_cast<int**>关键字, 将其转换为一个指向指针的指针

    reinterpret_cast<int**>(&derived)
    

    现在我们对这个指针(pA)的地址, 取其内容, 就会得到pA的内容

    std::cout << "derived对象中第一个指针的内容: " << *reinterpret_cast<int**>(&derived) << std::endl;
    

    根据上面的推测, 这个内容, 就是虚函数列表的地址

    控制台输出如下:

    通过vs2019中, 可以直接查看到derived的__vfptr对象的地址, 和控制台打印的内容是相同的

    2.单继承中虚函数列表的内容

    基类中有4个函数, 分别为

    f(); 
    g(); 
    h(); 
    i();
    

    派生类中有2个函数,分别为

    g();
    i1();
    

    现在使用表格的方式表示出来, 方便查看, 进行了override的函数, 会放在同一行

    结论 在虚函数列表中, 函数的布局如下图所示:

    代码验证请看链式继承中虚函数列表的内容

    3.链式继承中虚函数列表的内容

    声明3个类, 其继承关系为Derived继承Base2, Base2继承Base1

    class Base1 {
    public:
      virtual void f() { std::cout << "Base1::f" << std::endl; }
      virtual void g() { std::cout << "Base1::g" << std::endl; }
      virtual void h() { std::cout << "Base1::h" << std::endl; }
      virtual void i() { std::cout << "Base1::i" << std::endl; }
    };
    
    class Base2 : public Base1{
    public:
      virtual void f()override { std::cout << "Base2::f" << std::endl; }
      virtual void h1() { std::cout << "Base2::h1" << std::endl; }
    };
    
    class Derived : public Base2 {
    public:
      virtual void g()override { std::cout << "Derived::g" << std::endl; }
      virtual void i1() { std::cout << "Derived::i1" << std::endl; }
    };
    

    用表格的方法表示为:

    !()(https://silenzio-markdown-image-hosting-service.oss-cn-beijing.aliyuncs.com/博客图床/C%2B%2B 链��%继承下的虚函数列表/1a885406e3318ccbbf5dce9961e1599.png)

    结论

    在虚函数列表中, 函数的布局如下图所示:

    Derive只有一个虚函数表, 是在Base2的虚函数表上, 进行类似于单继承的覆盖
    同理, Base2也有一张虚函数表, 是在Base1的虚函数表上, 进行单继承的覆盖
    

    代码验证

    ////////////////////////////////////////////////////////////////////////////////
    // 链式继承
    ////////////////////////////////////////////////////////////////////////////////
    #include <iostream>
    class Base1 {
    public:
      virtual void f() { std::cout << "Base1::f" << std::endl; }
      virtual void g() { std::cout << "Base1::g" << std::endl; }
      virtual void h() { std::cout << "Base1::h" << std::endl; }
      virtual void i() { std::cout << "Base1::i" << std::endl; }
    };
    
    class Base2 : public Base1{
    public:
      virtual void f()override { std::cout << "Base2::f" << std::endl; }
      virtual void h1() { std::cout << "Base2::h1" << std::endl; }
    };
    
    class Derived : public Base2 {
    public:
      virtual void g()override { std::cout << "Derived::g" << std::endl; }
      virtual void i1() { std::cout << "Derived::i1" << std::endl; }
    };
    
    using Fun = void(*)(void);
    
    int main()
    {
      Fun pFun = nullptr;
    
      // 操作系统: windows 10 专业版 32/64位都可以
      // 编译器 : Visual Studio 2019 Community
      std::cout << sizeof(int) << std::endl; //  32位:4  64位:4
      std::cout << sizeof(long) << std::endl; // 32位:4  64位:4
      std::cout << sizeof(int*) << std::endl; // 32位:4  64位:8
    
      std::cout << "-------------------------------------------------------------------------------------- " << std::endl;
      std::cout << "Base1的虚表如下 " << std::endl;
      Base1 base1;
    
      std::cout << "base1对象的地址: " << (&base1) << std::endl;
      std::cout << "base1对象中第一个指针的地址(不是内容): " << (&base1) << std::endl;
      // &base1 是一个指向指针的指针
      // 对它进行*操作, 得到了这个指针的内容, 即它指向的位置, 是一个虚表(虚表的内容也是一堆指针)
      std::cout << "base1对象中第一个指针的内容: " << *reinterpret_cast<int**>(&base1) << std::endl;
      std::cout << "base1虚函数表地址: " << *reinterpret_cast<int**>(&base1) << std::endl;
      
      // 虚函数表地址, 也是一个指向指针的指针
      // 对它进行*操作, 得到了这个指针的内容, 即它指向的位置, 是一个函数指针
      std::cout << "base1虚函数表 — 第一个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1))))) << std::endl;
      pFun = reinterpret_cast<Fun>(* (reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)))));
      std::cout << "base1虚函数表 — 第一个函数内容:";
      pFun();                        // base1::f
      std::cout << std::endl;
    
      // 注意次数的偏移量, 32位偏移量是+1, 64位偏移量是+2
      std::cout << "base1虚函数表 — 第二个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1))+1*(sizeof(int*)/sizeof(int))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 1 * (sizeof(int*) / sizeof(int)))));
      std::cout << "base1虚函数表 — 第二个函数内容:";
      pFun();                        // base1::g
      std::cout << std::endl;
    
      std::cout << "base1虚函数表 — 第三个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 2 * (sizeof(int*) / sizeof(int))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 2 * (sizeof(int*) / sizeof(int)))));
      std::cout << "base1虚函数表 — 第三个函数内容:";
      pFun();                        // base1::h
      std::cout << std::endl;
    
      std::cout << "base1虚函数表 — 第四个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 3 * (sizeof(int*) / sizeof(int))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base1)) + 3 * (sizeof(int*) / sizeof(int)))));
      std::cout << "base1虚函数表 — 第四个函数内容:";
      pFun();                        // base1::i
      std::cout << std::endl;
    
      std::cout << "-------------------------------------------------------------------------------------- " << std::endl;
      std::cout << "Base2的虚表如下 " << std::endl;
      Base2 base2;
    
      std::cout << "base2对象的地址: " << (&base2) << std::endl;
      std::cout << "base2对象中第一个指针的地址(不是内容): " << (&base2) << std::endl;
      // &base1 是一个指向指针的指针
      // 对它进行*操作, 得到了这个指针的内容, 即它指向的位置, 是一个虚表(虚表的内容也是一堆指针)
      std::cout << "base2对象中第一个指针的内容: " << *reinterpret_cast<int**>(&base2) << std::endl;
      std::cout << "base2虚函数表地址: " << *reinterpret_cast<int**>(&base2) << std::endl;
    
      // 虚函数表地址, 也是一个指向指针的指针
      // 对它进行*操作, 得到了这个指针的内容, 即它指向的位置, 是一个函数指针
      std::cout << "base2虚函数表 — 第一个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)))));
      std::cout << "base2虚函数表 — 第一个函数内容:";
      pFun();                        // base2::f
      std::cout << std::endl;
    
      std::cout << "base2虚函数表 — 第二个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 1 * (sizeof(int*) / sizeof(int))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 1 * (sizeof(int*) / sizeof(int)))));
      std::cout << "base2虚函数表 — 第二个函数内容:";
      pFun();                        // base1::g
      std::cout << std::endl;
    
      std::cout << "base2虚函数表 — 第三个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 2 * (sizeof(int*) / sizeof(int))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 2 * (sizeof(int*) / sizeof(int)))));
      std::cout << "base2虚函数表 — 第三个函数内容:";
      pFun();                        // base1::h
      std::cout << std::endl;
    
      std::cout << "base2虚函数表 — 第四个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 3 * (sizeof(int*) / sizeof(int))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 3 * (sizeof(int*) / sizeof(int)))));
      std::cout << "base2虚函数表 — 第四个函数内容:";
      pFun();                        // base1::i
      std::cout << std::endl;
    
      std::cout << "base2虚函数表 — 第五个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 4 * (sizeof(int*) / sizeof(int))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&base2)) + 4 * (sizeof(int*) / sizeof(int)))));
      std::cout << "base2虚函数表 — 第五个函数内容:";
      pFun();                        // base2::h1
      std::cout << std::endl;
    
      std::cout << "-------------------------------------------------------------------------------------- " << std::endl;
      std::cout << "Derived的虚表如下 " << std::endl;
      Derived Derived;
    
      std::cout << "Derived对象的地址: " << (&Derived) << std::endl;
      std::cout << "Derived对象中第一个指针的地址(不是内容): " << (&Derived) << std::endl;
      // &base1 是一个指向指针的指针
      // 对它进行*操作, 得到了这个指针的内容, 即它指向的位置, 是一个虚表(虚表的内容也是一堆指针)
      std::cout << "Derived对象中第一个指针的内容: " << *reinterpret_cast<int**>(&Derived) << std::endl;
      std::cout << "Derived虚函数表地址: " << *reinterpret_cast<int**>(&Derived) << std::endl;
    
      // 虚函数表地址, 也是一个指向指针的指针
      // 对它进行*操作, 得到了这个指针的内容, 即它指向的位置, 是一个函数指针
      std::cout << "Derived虚函数表 — 第一个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)))));
      std::cout << "Derived虚函数表 — 第一个函数内容:";
      pFun();                        // base2::f
      std::cout << std::endl;
    
      std::cout << "Derived虚函数表 — 第二个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 1 * (sizeof(int*) / sizeof(int))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 1 * (sizeof(int*) / sizeof(int)))));
      std::cout << "Derived虚函数表 — 第二个函数内容:";
      pFun();                        // Derived::g
      std::cout << std::endl;
    
      std::cout << "Derived虚函数表 — 第三个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 2 * (sizeof(int*) / sizeof(int))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 2 * (sizeof(int*) / sizeof(int)))));
      std::cout << "Derived虚函数表 — 第三个函数内容:";
      pFun();                        // base1::h
      std::cout << std::endl;
    
      std::cout << "Derived虚函数表 — 第四个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 3 * (sizeof(int*) / sizeof(int))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 3 * (sizeof(int*) / sizeof(int)))));
      std::cout << "Derived虚函数表 — 第四个函数内容:";
      pFun();                        // base1::i
      std::cout << std::endl;
    
      std::cout << "Derived虚函数表 — 第五个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 4 * (sizeof(int*) / sizeof(int))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 4 * (sizeof(int*) / sizeof(int)))));
      std::cout << "Derived虚函数表 — 第五个函数内容:";
      pFun();                        // base2::h1
      std::cout << std::endl;
    
      std::cout << "Derived虚函数表 — 第六个函数地址:" << (*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 5 * (sizeof(int*) / sizeof(int))))) << std::endl;
      pFun = reinterpret_cast<Fun>(*(reinterpret_cast<int**>(*(reinterpret_cast<int**>(&Derived)) + 5 * (sizeof(int*) / sizeof(int)))));
      std::cout << "Derived虚函数表 — 第六个函数内容:";
      pFun();                        // Derived::i1
      std::cout << std::endl;
    
      return 0;
    }
    
  • 相关阅读:
    mysql存储过程基本函数
    Java多线程程序设计详细解析
    手把手教你写Undo、Redo程序
    mysql存储过程学习总结-操作符
    深入解析ATL第二版(ATL8.0)笔记--(2.3节)
    mysql 5.0存储过程学习总结
    php判断浏览器和语言
    Windows7系统环境安装配置PHP开发环境
    Nginx环境下Php安装
    php学习
  • 原文地址:https://www.cnblogs.com/silenzio/p/12205888.html
Copyright © 2020-2023  润新知