• c++多态和虚函数表实现原理


     

    自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:

    https://www.cnblogs.com/bclshuai/p/11380657.html

    C++多态以及虚函数表实现原理

     

    目录

    1      定义

    2      虚函数表实现原理

    3      实例解析

    3.1     定义父类

    3.2     父类对象地址空间剖析

    3.3     子类继承父类的虚函数表

    3.3.1     单继承覆盖和不覆盖对比

    3.3.2     子类继承多个父类

    3.3.3     多层继承的子类

    1        定义

    C++中的虚函数的作用主要是实现了多态的机制。关于多态,子类对象赋值给父类指针,然后通过父类的指针调用实际子类覆盖父类的虚函数实现。赋值不同的子类对象给父类指针,可以使用父类指针调用不同子类的虚函数(必须是覆盖父类的函数),这种技术可以让父类的指针有“多种形态”。

    2        虚函数表实现原理

    每个子类对象创建时,会有一个虚函数表,而且虚函数表在对象首地址开始,以达到高效访问的目的。一个子类继承多个父类时,子类对象的头部有多个虚函数表,把父类的虚函数表复制过来,按照继承顺序排列。如果子类覆盖了父类的虚函数,则替换掉复制过来的父类虚函数表中对应位置的虚函数。子类中的自定义虚函数,在第一个虚函数表中,排在父类虚函数后面(主流说法是这样,但是实际测试,并没有)。当用子类的对象赋值给父类指针时,父类指针指向了子类虚函数表,当调用函数时,就会从子类虚函数表中查找函数,去调用子类覆盖父类的虚函数,这样就实现了多态。

    3        实例解析

    3.1   定义父类

    class Base {

                public:

                virtual void f() { cout << "Base::f" << endl; }

                virtual void g() { cout << "Base::g" << endl; }

                virtual void h() { cout << "Base::h" << endl; }

    int a=0;

    };

    3.2   父类对象地址空间剖析

    创建一个对象Base b;

    父类的虚函数表示意图为

     

     虚函数表在对象地址首部,函数指针按照定义的顺序在虚函数表中,可以通过地址访问的方式去访问这些函数。

    int main()

    {

           typedef void(*Fun)(void);

     

           Base b;

           cout << "虚函数表地址:" << (int*)(&b) << endl;

           cout << "1" << *(int*)(&b) << endl;

           cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;

           Fun funf = ((Fun)*((int*)*(int*)(&b) + 0));

           Fun fung= ((Fun)*((int*)*(int*)(&b) + 1));

           Fun funh= ((Fun)*((int*)*(int*)(&b) + 2));

           funf();

           fung();

           funh();

           system("pause");

     

        return 0;

    }

    输出结果

     

    b地址空间如下图所示:

    理解下面一句是怎么获取到函数地址的。

    pFun = (Fun)*((int*)*(int*)(&b));

    (&b)获取到对象b的地址。

    (int*)(&b))转化为int*的指针,对象b的前4个字节是虚函数表的地址,int* 也是四个字节,这种强制转换,是把&a开始的4个字节当作一个整体,也就是虚函数表的地址,虚函数表地址在对象地址空间首部。

     *(int*)(&b))  *虚函数表地址,得到的是虚函数表的内存空间,虚函数表内存空间首部是虚函数f的地址,即Base::f()的地址。

     ((int*)*(int*)(&b))在转换为int*类型的指针,就得到了四个字节的虚函数f的地址。f,g,h的地址各占四个字节,int*的指针+1,就是向后移动四个字节,得到g函数的地址,+2,就是再向后移动四个字节,得到h函数的地址。

    *((int*)*(int*)(&b))  *虚函数地址,得到的是虚函数f的实际的存储空间。

    (Fun)*((int*)*(int*)(&b));转换为函数指针。

     

    思考:如果虚函数fgh是private私有的,那么通过这种方式仍然可以获取到私有函数的地址fgh,并且去访问这些私有函数。

    3.3   子类继承父类的虚函数表

    3.3.1         单继承覆盖和不覆盖对比

    class Derver: public Base{

    public:

           virtual void f1() { cout << "Derver::f" << endl; }

     

           virtual void g1() { cout << "Derver::g" << endl; }

     

           virtual void h1() { cout << "Derver::h" << endl; }

    };

    查看无覆盖和有覆盖继承的情况,右边子类覆盖了父类的f函数。

     

    无覆盖的子类对象地址空间。网上说父类虚函数在虚表前面,子类虚函数在后面

     

    但是实际情况是虚函数表中只有父类虚函数,如下所示

     

    有覆盖的子类对象地址空间,子类虚函数表的首位替换了子类覆盖的函数

     

    虚函数表中是子类虚函数替换了父类虚函数的位置,也没有未覆盖的子类虚函数f1和g1。

     

    总结:单继承的子类对象虚函数表中,只包含父类对象的虚函数和覆盖的子类虚函数,子类虚函数会替换掉对应位置的父类虚函数。子类自定义的虚函数不在虚函数表中。

    3.3.2         子类继承多个父类

    (1)   定义一个子类继承三个基类,继承关系如下图

     

    代码实现

    class Base1 {

    public:

           virtual void f() { cout << "Base1::f" << endl; }

     

           virtual void g() { cout << "Base1::g" << endl; }

     

           virtual void h() { cout << "Base1::h" << endl; }

    };

    class Base2 {

    public:

           virtual void f() { cout << "Base2::f" << endl; }

     

           virtual void g() { cout << "Base2::g" << endl; }

     

           virtual void h() { cout << "Base2::h" << endl; }

    };

    class Base3 {

    public:

           virtual void f() { cout << "Base3::f" << endl; }

     

           virtual void g() { cout << "Base3::g" << endl; }

     

           virtual void h() { cout << "Base3::h" << endl; }

    };

    class Mult :public Base1, public Base2,public Base3 {

    public:

           virtual void f() { cout << "Mult::f" << endl; }

     

           virtual void g1() { cout << "Mult::g" << endl; }

     

    };

    class Grander :public Derver

    {

    public:

           virtual void f1() { cout << "Grander::f" << endl; }

     

           virtual void g1() { cout << "Grander::g1" << endl; }

    };

    (2)   查看子类对象的地址空间

    如下图所示,子类对象空间有三个基类虚表,子类覆盖的函数f替换了虚表中对应的位置。子类自有的虚函数网上说是在第一个虚表后面。但是实际却没有。

     

    如下图所示,是实际的地址空间,第一个虚表中并没有看到子类的虚函数g1.

     

    总结:子类继承多个父类,子类对象中包含了三个父类的虚函数表,按照继承顺序排列。子类覆盖的虚函数会替换对应位置的三个父类虚函数。子类自定义的虚函数也不在虚函数表中。

    3.3.3         多层继承的子类

    子类Derver继承父类Base,然后Grander类继承Derver类。

    代码实现如下

    class Base {

    private:

        virtual void f() { cout << "Base::f" << endl; }

     

        virtual void g() { cout << "Base::g" << endl; }

     

        virtual void h() { cout << "Base::h" << endl; }

        int a = 0;

    };

    class Derver: public Base{

    public:

        virtual void f() { cout << "Derver::f" << endl; }

        virtual void g1() { cout << "Derver::g1" << endl; }

        virtual void h1() { cout << "Derver::h1" << endl; }

        int d =1;

    };

    class Grander :public Derver

    {

    public:

        virtual void f() { cout << "Grander::f" << endl; }

        virtual void g1() { cout << "Grander::g1" << endl; }

    };

    内存虚表结构如下所示,只虚表中只有有Base类中函数Base::g(),Base::h(),还有覆盖的子类函数Grander::f()。其他的函数都没有。连覆盖Derver的Grander::g1()都没有。这是什么鬼?

     

    总结:多层继承,子类对象的虚函数表中值包含Base类的虚函数,子类覆盖的虚函数替换掉对应位置的Base类虚函数。中间的Derver虚函数和Grander中自定义的虚函数不存在虚函数表中。

    (1)基类Base指针指向对象Derver

    Base* pB = new Derver();

    得到的内存结构如下图所示

    有两个虚拟表,虚函数表中地址相同,函数是基类的虚函数和Derver覆盖基类的虚函数。

     

    (2)中间的指针Derver*指向孙类对象Grander

    得到的虚表结构如下,虚函数是基类base虚函数和Grander覆盖基类的虚函数Grander::f();

    Derver* pD  = new Grander();

     

    (3)基类Base指针指向孙类对象Grander

    得到的虚函数表如下图所示

    Base* pDG = new Grander();

     

    总结:多层继承时,虚函数表中保存的是基类的虚函数Base和覆盖基类的虚函数。虚函数表会保留继承层级关系。会有两个虚函数表,表中的函数地址相同。

     

  • 相关阅读:
    Linux内核分析——第四章 进程调度
    Linux内核分析——可执行程序的装载
    Linux内核分析——第七章 链接
    Linux内核分析——第三章 进程管理
    Linux内核分析——进程的描述和进程的创建
    Linux内核分析——第十八章 调试
    Linux内核分析——第五章 系统调用
    20145201李子璇 《网络对抗》 Web安全基础实践
    20145201李子璇 《网络对抗》 Web基础
    20145201 李子璇 《网络对抗》网络欺诈技术防范
  • 原文地址:https://www.cnblogs.com/bclshuai/p/13947597.html
Copyright © 2020-2023  润新知