• c++中虚多态的实现机制


    c++中虚多态的实现机制


    參考博客:http://blog.csdn.net/neiloid/article/details/6934135

    1. 序言
    2. 证明vptr指针存在
    3. 无继承
    4. 单继承无覆盖
    5. 单继承有覆盖
    6. 多继承有覆盖
    7. 总结

    1.序言
    这篇博文探讨c++内部多态的实现机制,參考书主要是《深入探索c++对象模型》。因为本人水平有限,望读者指正。


    实现多态有3点前提,即继承。虚化,基类指针指向派生类对象。
    本质而言,整体来说,当类的内部通过virtual关键字声明虚函数时。编译器生成vptr指针,指向虚函数表(虚表),虚表中依次存放着各虚函数(非虚函数不在这里)。

    这样。就能够通过指针来调用这些函数。


    PS:执行环境为VC++6.0。因为编译器的差异而产生的不同本文不讨论。


    大概的内存布局


    2.证明vptr指针的存在

    #include<iostream>
    using namespace std;
    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 main (void)
    {
        Base b; 
        cout<<"证明vptr的存在"<<endl;
        cout<<"sizeof b = " <<sizeof b<<endl; 
        return 0;
    }

    执行结果
    ps:linux下 sizeof b = 8


    3无继承

    #include<iostream>
    using namespace std;
    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 main (void)
    {
        typedef unsigned long P_FUN;//指针类型为无符号长整型
        typedef void(*FUN)(void);//声明函数指针
    
        FUN f,g,h;
        Base b; 
    
        f=(FUN)(*(P_FUN*)*(P_FUN*)&b);
        g=(FUN)(*((P_FUN*)*(P_FUN*)&b+1));
        h=(FUN)(*((P_FUN*)*(P_FUN*)&b+2));
    
        f();
        g();
        h();
    
        cout<<(FUN)(*((P_FUN*)*(P_FUN*)&b+3))<<endl;
        return 0;
    }

    执行结果
    这样就通过指针的方式调用了续表中的方法函数。


    通过现象。能够大概预计出内存的布局。例如以下图。
    结束位置以后可能为操作系统内部的仅仅读空间,訪问会发生段错误。


    ps:linux下结束位置可能为0。可能为1,有兴趣的读着能够研究一下。


    内存布局


    4.单继承无覆盖

    #include<iostream>
    using namespace std;
    class Base
    {
    public:
        virtual void f(){cout<<"Base::f()"<<endl;}
        virtual void g(){cout<<"Base::g()"<<endl;}
        virtual void h(){cout<<"Base::h()"<<endl;}
    
    };
    class Derive:public Base
    {
    public:
        virtual void f1(){cout<<"Derive::f1()"<<endl;}
        virtual void g1(){cout<<"Derive::g1()"<<endl;}
        virtual void h1(){cout<<"Derive::h1()"<<endl;}
    
    };
    int main (void)
    {
        typedef unsigned long P_FUN;
        typedef void(*FUN)(void);
    
        FUN f,g,h,f1,g1,h1;
        Derive b;
    
        f=(FUN)(*(P_FUN*)*(P_FUN*)&b);
        g=(FUN)(*((P_FUN*)*(P_FUN*)&b+1));
        h=(FUN)(*((P_FUN*)*(P_FUN*)&b+2));
        f1=(FUN)(*((P_FUN*)*(P_FUN*)&b+3));
        g1=(FUN)(*((P_FUN*)*(P_FUN*)&b+4));
        h1=(FUN)(*((P_FUN*)*(P_FUN*)&b+5));
    
        f();
        g();
        h();
        f1();
        g1();
        h1();
    
        cout<<(FUN)(*((P_FUN*)*(P_FUN*)&b+6))<<endl;
        return 0;
    }

    执行结果
    依据结果能够看出,无覆盖的情况下,派生类的虚函数直接放在第一张虚表后边。
    内存布局


    5.单继承有覆盖

    #include<iostream>
    using namespace std;
    class Base
    {
    public:
        virtual void f(){cout<<"Base::f()"<<endl;}
        virtual void g(){cout<<"Base::g()"<<endl;}
        virtual void h(){cout<<"Base::h()"<<endl;}
    
    };
    class Derive:public Base
    {
    public:
        virtual void f(){cout<<"Derive::f1()"<<endl;}
        virtual void g1(){cout<<"Derive::g1()"<<endl;}
        virtual void h1(){cout<<"Derive::h1()"<<endl;}
    
    };
    int main (void)
    {
        typedef unsigned long P_FUN;
        typedef void(*FUN)(void);
    
        FUN f,g,h,f1,g1,h1;
        Derive b;
    
        f1=(FUN)(*(P_FUN*)*(P_FUN*)&b);
        g=(FUN)(*((P_FUN*)*(P_FUN*)&b+1));
        h=(FUN)(*((P_FUN*)*(P_FUN*)&b+2));
        g1=(FUN)(*((P_FUN*)*(P_FUN*)&b+3));
        h1=(FUN)(*((P_FUN*)*(P_FUN*)&b+4));
    
        f1(); 
        g();
        h();
        g1();
        h1();
    
        cout<<(FUN)(*((P_FUN*)*(P_FUN*)&b+5))<<endl;
        return 0;
    }

    执行结果
    依据执行结果能够看出,有覆盖的情况下,派生类虚函数覆盖到基类的虚函数,不覆盖的虚函数依次放在第一张表的后面。
    内存布局

    6多继承有覆盖

    #include<iostream>
    using namespace std;
    class Base1
    {   
    private:
        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 Derive:public Base1,public Base2
    {
    public:
        virtual void f(){cout<<"Derive::f1()"<<endl;}
        virtual void g1(){cout<<"Derive::g1()"<<endl;}
        virtual void h1(){cout<<"Derive::h1()"<<endl;}
    };
    int main (void)
    {
        typedef unsigned long P_FUN;
        typedef void(*FUN)(void);
    
        FUN B1f,B1g,B1h,B2f,B2g,B2h,Dg,Dh;
        Derive b;
    
        B1f=(FUN)(*(P_FUN*)*((P_FUN*)&b));
        B1g=(FUN)(*((P_FUN*)*((P_FUN*)&b)+1));
        B1h=(FUN)(*((P_FUN*)*((P_FUN*)&b)+2));
    
        B2f=(FUN)(*(P_FUN*)*((P_FUN*)&b+1));
        B2g=(FUN)(*((P_FUN*)*((P_FUN*)&b+1)+1));
        B2h=(FUN)(*((P_FUN*)*((P_FUN*)&b+1)+2));
    
        Dh=(FUN)(*((P_FUN*)*((P_FUN*)&b)+3));
        Dg=(FUN)(*((P_FUN*)*((P_FUN*)&b)+4));
    
        B1f();
        B1g();
        B1h();
        cout<<endl;
    
        Dh();
        Dg();
        cout<<endl;
    
        B2f();
        B2g();
        B2h();
        cout<<endl;
    
        cout<<(FUN)(*((P_FUN*)*((P_FUN*)&b)+5))<<endl;
        cout<<(FUN)(*((P_FUN*)*((P_FUN*)&b+1)+3))<<endl;
    
        return 0;
    }
    

    执行结果
    依据结果可知,多继承下,覆盖的派生类函数会把全部的虚表中相相应的基类虚函数覆盖掉,不覆盖的派生类虚函数仅仅放在第一张虚表的后面。其它虚表后没有。
    内存布局


    7.总结
    1.当类的内部通过virtual关键字声明虚函数时,编译器生成vptr指针,指向虚函数表(虚表),虚表中依次存放着各虚函数(非虚函数不在当中)。


    2.无覆盖的情况下。派生类的虚函数直接放在第一张虚表后边。
    3.有覆盖的情况下。派生类虚函数覆盖到基类的虚函数,不覆盖的虚函数依次放在第一张表的后面。


    4.多继承下,覆盖的派生类函数会把全部的虚表中相相应的基类虚函数覆盖掉,不覆盖的派生类虚函数仅仅放在第一张虚表的后面,其它虚表后没有。

    PS:c++是一门威力十分强大的语言。内部构造复杂,或许有人说这些内容没什么卵用,但本人认为深入理解一下绝不是坏事,本文阅读的难点在于指针的应用,事实上整个c/c++的强大也是得益于指针,希望大家通过本文对c/c++有更深入的理解。
    另外说明一下。指针确实是不安全的,比如本文中全部的public关键字删掉之后,通过指针仍然能够调用出类内的私有成员方法。

    本人是一个即将大三的本科生,能力有限。博文如有不妥支出望各位读者指正。

  • 相关阅读:
    冒泡法排序(整数)
    system函数的应用
    数数的位数(正整数)
    翻译Sencha Touch Architecture 第二章
    BeeFramework
    Creating a masterdetail application with tableview
    翻译Sencha Touch Mobile Javascript Framework 第八章 The Flickr Finder Application
    SDWebImage笔记
    Ubuntu aptget方式安装Subversion
    Python统计svn变更代码行数
  • 原文地址:https://www.cnblogs.com/zsychanpin/p/7137205.html
Copyright © 2020-2023  润新知