zoukankan      html  css  js  c++  java
  • 虚函数表

    ******************第二次*******************************

    虚函数表。就是所有类,包括基类,派生类。都拥有的各自的 函数指针数组。

    存放的是对于这个类来说,实际的所有函数指针地址。很明显,派生类数据更多,应为拥有自己独有的函数地址。

    多继承,就有多个。为什么?因为派生类用基类指针的时候, 需要这个基类的方法,而每个基类方法是独立的。所有多个表,但是被重写的方法,存储的还是派生类的。 派生类独有的方法,特别的放置在第一个虚函数表中。

    Base中虚函数表结构:

    Derive中虚函数表结构:

    具体看。

    #include <stdio.h>
    #include <iostream>
    using namespace std;
    
    
    
    //每个函数都有地址.调用函数时,就压参数,机器状态等,后直接jump到指令.
    //类也是一样.多压一个this.指针.有一个疑问,如果一个类,无任何数据,无虚方法,无继承,对象的地址有什么用?
    //但是有虚函数后,重载,有同名函数,编译器起初不知道调用哪个函数.编译器就在类的数据存放前.加一个虚函数表地址.
    //只要是类继承了其他含有虚方法的类,类的数据前就加入一个指针,指向虚函数表.虚函数表存放了了此类调用的所有方法的实际地址.
    
    class Base {
    public:
    int a;
    virtual void f() { cout << "Base::f" << endl; }//在Base的虚函数表中.不在Derive中.被继承类覆盖了.
    void g() { cout << "Base::g" << endl; }//不在Base的虚函数表中.当然也不再Derive中.
    virtual void h() { cout << "Base::h" << endl; }//在Derive的虚函数表中.按继承顺序覆盖了后面的基类base的h方法.
    Base():a(1){}
    Base(const Base& _p)
    {
        a=_p.a;
    }
    };
    
    
    class Base2 {
    public:
    int a;
    virtual void f() { cout << "Base2::f" << endl; }//在Base的虚函数表中.不在Derive中.被继承类覆盖了.
    virtual void g1() { cout << "Base2::g1" << endl; }//在Base的虚函数表中.在Derive中.
    virtual void h() { cout << "Base2::h" << endl; }//不在Derive的虚函数表中.按继承顺序覆盖了.
    Base2():a(2){}
    };
    
    
    class Derive : public Base,public Base2{
    public:
    int a;
    virtual void f() { cout << "Derive::f" << endl; }//在Derive的虚函数表中.覆盖了基类的.
    virtual void g1() { cout << "Derive::g1" << endl; }//在Derive的虚函数表中.覆盖了基类的.
    virtual void e(){cout << "mysefl e" << endl;};//在Derive的虚函数表中.
    Derive():a(3){}
    };
    
    
    
    int main (int argc,char *argv[])
    {
    typedef void(*Fun)(void);
    
    Base b;
    
    Fun pFun = NULL;
    
    printf( "对象b的内存地址,也就是class类base1的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x
    " , &b,*(int *)&b );
    printf( "对象b的数据a, 地址:%0x, value:%d
    " , &(b.a),*&(b.a));
    printf( "类base1的虚函数表 — 第一个函数地址:%0x
    " , *(int *)*(int *)(&b) );
    pFun = (Fun) *(int *)*(int *)(&b);
    pFun();
    printf( "类base1的虚函数表 — 第二个函数地址:%0x
    " , *(int *)(*(int *)(&b)+4) );
    pFun = (Fun) *(int *)(*(int *)(&b)+4);
    pFun();
    printf( "类base1的虚函数表 — 第三个函数地址:%0x
    " , *(int *)(*(int *)(&b)+8) );
    
    printf( "类base1的虚函数表 — 第四个函数地址:%0x
    " , *(int *)(*(int *)(&b)+12) );
    printf( "类base1的虚函数表 — 第五个函数地址:%0x
    
    " , *(int *)(*(int *)(&b)+16) );
    
    
    
    
    Base2 b2;
    
    
    printf( "对象b的内存地址,也就是class类base2的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x
    " , &b2,*(int *)&b2 );
    printf( "对象b的数据a, 地址:%0x, value:%d
    " , &(b2.a),*&(b2.a));
    
    printf( "类base2的虚函数表 — 第一个函数地址:%0x
    " , *(int *)*(int *)(&b2) );
    pFun = (Fun) *(int *)(*(int *)(&b2)+0);
    pFun();
    printf( "类base2的虚函数表 — 第二个函数地址:%0x
    " , *(int *)(*(int *)(&b2)+4) );
    pFun = (Fun) *(int *)(*(int *)(&b2)+4);
    pFun();
    printf( "类base2的虚函数表 — 第三个函数地址:%0x
    " , *(int *)(*(int *)(&b2)+8) );
    pFun = (Fun) *(int *)(*(int *)(&b2)+8);
    pFun();
    printf( "类base2的虚函数表 — 第四个函数地址:%0x
    
    " , *(int *)(*(int *)(&b2)+12) );
    
    
    printf( "类base2的虚函数表 — 第五个函数地址:%0x
    
    " , *(int *)(*(int *)(&b2)+16) );
    
    
    Derive d;
    
    
    
    printf( "对象d的内存地址,也就是class类Derive的第一张虚拟表的地址的地址 :%0x,虚拟表的地址:%0x
    " , &d,*(int *)&d );
    printf( "第一基类的数据地址 :%0x,数据:%0x
    " , (int *)(&d)+1,*((int *)(&d)+1) );
    printf( "对象d的内存地址,也就是class类Derive的第二张虚拟表的地址的地址 :%0x,虚拟表的地址:%0x
    " , (int *)(&d)+2,*((int *)(&d)+2) );
    printf( "第一基类的数据地址 :%0x,数据:%0x
    " , (int *)(&d)+3,*((int *)(&d)+3) );
    printf( "对象d的数据a, 地址:%0x, value:%d
    " , &(d.a),*&(d.a));
    
    
    printf( "多重继承虚函数表 — 第一张虚拟表base1第一个函数地址:%0x
    " , *(int *)*(int *)(&d) );//测试发现,用了自己的地址.
    pFun = (Fun) *(int *)*(int *)(&d);
    pFun();
    
    printf( "多重继承虚函数表 — 第一张虚拟表base1第二个函数地址:%0x
    " , *(int *)(*(int *)(&d)+4) );//用了b1的第二函数.
    pFun = (Fun) *(int *)(*(int *)(&d)+4);
    pFun();
    printf( "多重继承虚函数表 — 第一张虚拟表base1第三个函数地址:%0x
    " , *(int *)(*(int *)(&d)+8) );//用了b1的第三函数.
    pFun = (Fun) *(int *)(*(int *)(&d)+8);
    pFun();
    printf( "多重继承虚函数表 — 第一张虚拟表base1第四个函数地址:%0x
    " , *(int *)(*(int *)(&d)+12) );//测试发现,用了自己的地址.
    pFun = (Fun) *(int *)(*(int *)(&d)+12);
    pFun();
    printf( "多重继承虚函数表 — 第一张虚拟表base1第四个函数地址:%0x
    " , *(int *)(*(int *)(&d)+12) );//测试发现,用了自己的地址.
    
    printf( "多重继承虚函数表 — 第一张虚拟表base1第五个函数地址?:%0x
    " , *(int *)(*(int *)(&d)+16) );//第一个虚拟表,必须没有第5个函数.
    
    
    
    printf( "多重继承虚函数表 — 第二张虚拟表base1第一个函数地址:%0x
    " , *(int *)*((int *)(&d)+2) );//测试发现,用了自己的地址.
    pFun = (Fun) *(int *)*((int *)(&d)+2);
    pFun();
    
    printf( "多重继承虚函数表 — 第二张虚拟表base1第二个函数地址:%0x
    " , *(int *)(*((int *)(&d)+2)+4)   );//测试发现,用了自己的地址.
    pFun = (Fun) *(int *)(*((int *)(&d)+2)+4);
    pFun();
    
    
    printf( "多重继承虚函数表 — 第二张虚拟表base1第3个函数地址:%0x
    " , *(int *)(*((int *)(&d)+2)+8)   );//测试发现,用了自己的地址.
    pFun = (Fun) *(int *)(*((int *)(&d)+2)+8);
    pFun();
    
    printf( "多重继承虚函数表 — 第二张虚拟表base1第4个函数地址:%0x
    " , *(int *)(*((int *)(&d)+2)+12)   );//测试发现,用了自己的地址.
    
    
    printf( "多重继承虚函数表 — 第二张虚拟表base1第5个函数地址?:%0x
    " , *(int *)(*((int *)(&d)+2)+16)   );//测试发现,用了自己的地址.
    
    
    
    printf("***********************
    ");
    
    d.f();//属于子类的2个父类的虚拟表地址.都包含
    d.e();
    d.g();
    d.g1();
    d.Base2::h();
    
    
    cout<<"本质上,对象是体现不了多态的.只有指针才能体现多态.Base *pb,调用pd的f方法."<<endl;
    Base *pb=&d;
    pb->f();//到这里就会找到d的地址,通过之后找到class derive的虚函数表.
    
    
    
    Base bb2= (Base)d ;//测试发现,强制转换是调用复制构造函数.
    cout<<bb2.a<<endl;
    bb2.f();
    Base bb3= static_cast<Base>(d);//也是一样,调用复制构造函数.不过这个书上说会有安全检测.没验证.
    cout<<bb3.a<<endl;
    bb3.f();
    
    //Derive dd2= (Derive)bb2;//从基类到派生类,一样,都是需要构造函数,如果有基类为参的派生类构造函数.也行.
    //Derive dd2= static_cast<Derive>(bb2);//从基类到派生类,一样,都是需要构造函数,如果有基类为参的派生类构造函数.也行.
    
    
    return 0;
    }
    #include <stdio.h>
    #include <iostream>
    using namespace std;
    
    
    
    //每个函数都有地址.调用函数时,就压参数,机器状态等,后直接jump到指令.
    //类也是一样.多压一个this.指针.有一个疑问,如果一个类,无任何数据,无虚方法,无继承,对象的地址有什么用?
    //但是有虚函数后,重载,有同名函数,编译器起初不知道调用哪个函数.编译器就在类的数据存放前.加一个虚函数表地址.
    //只要是类继承了其他含有虚方法的类,类的数据前就加入一个指针,指向虚函数表.虚函数表存放了了此类调用的所有方法的实际地址.
    
    class Base {
    public:
    int a;
    virtual void f() { cout << "Base::f" << endl; }//在Base的虚函数表中.不在Derive中.被继承类覆盖了.
    void g() { cout << "Base::g" << endl; }//不在Base的虚函数表中.当然也不再Derive中.
    virtual void h() { cout << "Base::h" << endl; }//在Derive的虚函数表中.按继承顺序覆盖了后面的基类base的h方法.
    Base():a(1){}
    
    
    };
    
    
    class Base2 {
    public:
    int a;
    virtual void f() { cout << "Base2::f" << endl; }//在Base的虚函数表中.不在Derive中.被继承类覆盖了.
    virtual void g1() { cout << "Base2::g1" << endl; }//在Base的虚函数表中.在Derive中.
    virtual void h() { cout << "Base2::h" << endl; }//不在Derive的虚函数表中.按继承顺序覆盖了.
    Base2():a(2){}
    
    
    };
    
    
    class Derive : public Base,public Base2{
    public:
    int a;
    virtual void f() { cout << "Derive::f" << endl; }//在Derive的虚函数表中.覆盖了基类的.
    virtual void g1() { cout << "Derive::g1" << endl; }//在Derive的虚函数表中.覆盖了基类的.
    virtual void e(){cout << "mysefl e" << endl;};//在Derive的虚函数表中.
    Derive():a(3){}
    };
    
    
    
    int main (int argc,char *argv[])
    {
    typedef void(*Fun)(void);
    
    Base b;
    
    Fun pFun = NULL;
    
    printf( "对象b的内存地址,也就是class类base1的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x
    " , &b,*(int *)&b );
    printf( "对象b的数据a, 地址:%0x, value:%d
    " , &(b.a),*&(b.a));
    printf( "类base1的虚函数表 — 第一个函数地址:%0x
    " , *(int *)*(int *)(&b) );
    pFun = (Fun) *(int *)*(int *)(&b);
    pFun();
    printf( "类base1的虚函数表 — 第二个函数地址:%0x
    " , *(int *)(*(int *)(&b)+4) );
    pFun = (Fun) *(int *)(*(int *)(&b)+4);
    pFun();
    printf( "类base1的虚函数表 — 第三个函数地址:%0x
    " , *(int *)(*(int *)(&b)+8) );
    
    printf( "类base1的虚函数表 — 第四个函数地址:%0x
    " , *(int *)(*(int *)(&b)+12) );
    printf( "类base1的虚函数表 — 第五个函数地址:%0x
    
    " , *(int *)(*(int *)(&b)+16) );
    
    
    
    
    Base2 b2;
    
    
    printf( "对象b的内存地址,也就是class类base2的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x
    " , &b2,*(int *)&b2 );
    printf( "对象b的数据a, 地址:%0x, value:%d
    " , &(b2.a),*&(b2.a));
    
    printf( "类base2的虚函数表 — 第一个函数地址:%0x
    " , *(int *)*(int *)(&b2) );
    pFun = (Fun) *(int *)(*(int *)(&b2)+0);
    pFun();
    printf( "类base2的虚函数表 — 第二个函数地址:%0x
    " , *(int *)(*(int *)(&b2)+4) );
    pFun = (Fun) *(int *)(*(int *)(&b2)+4);
    pFun();
    printf( "类base2的虚函数表 — 第三个函数地址:%0x
    " , *(int *)(*(int *)(&b2)+8) );
    pFun = (Fun) *(int *)(*(int *)(&b2)+8);
    pFun();
    printf( "类base2的虚函数表 — 第四个函数地址:%0x
    
    " , *(int *)(*(int *)(&b2)+12) );
    
    
    printf( "类base2的虚函数表 — 第五个函数地址:%0x
    
    " , *(int *)(*(int *)(&b2)+16) );
    
    
    Derive d;
    
    
    
    printf( "对象d的内存地址,也就是class类Derive的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x
    " , &d,*(int *)&d );
    printf( "对象d的数据a, 地址:%0x, value:%d
    " , &(d.a),*&(d.a));
    
    
    printf( "多重继承虚函数表 — 第一张虚拟表base1第一个函数地址:%0x
    " , *(int *)*(int *)(&d) );//测试发现,用了自己的地址.
    pFun = (Fun) *(int *)*(int *)(&d);
    pFun();
    
    printf( "多重继承虚函数表 — 第一张虚拟表base1第二个函数地址:%0x
    " , *(int *)(*(int *)(&d)+4) );//用了b1的第二函数.
    pFun = (Fun) *(int *)(*(int *)(&d)+4);
    pFun();
    printf( "多重继承虚函数表 — 第一张虚拟表base1第三个函数地址:%0x
    " , *(int *)(*(int *)(&d)+8) );//用了b1的第三函数.
    pFun = (Fun) *(int *)(*(int *)(&d)+8);
    pFun();
    printf( "多重继承虚函数表 — 第一张虚拟表base1第四个函数地址:%0x
    " , *(int *)(*(int *)(&d)+12) );//测试发现,用了自己的地址.
    pFun = (Fun) *(int *)(*(int *)(&d)+12);
    pFun();
    
    
    
    d.f();//属于子类的2个父类的虚拟表地址.都包含
    d.e();
    d.g();
    d.g1();
    d.Base2::h();
    
    
    cout<<"本质上,对象是体现不了多态的.只有指针才能体现多态.Base *pb,调用pd的f方法."<<endl;
    Base *pb=&d;
    pb->f();//到这里就会找到d的地址,通过之后找到class derive的虚函数表.
    
    
    return 0;
    }

    对于派生类的虚表。

    1)  对于每个基类都生成一份虚表,

    2)  子类的成员函数被放到了第一个基类的虚表中。

    3)  内存布局中,其基类虚标依次按声明顺序排列。

    4)  每个基类的虚表中的f()函数都被overwrite成了派类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

  • 相关阅读:
    个人第三次作业——原型设计
    《构建之法》团队作业第一次
    vsCode如何将结果输入到调试控制台
    Beta-冲刺第三天
    Beta版本(有更改)
    Beta冲刺-第二天
    Beta冲刺—第一天
    个人作业-测试
    团队项目—系统设计
    团队项目-需求分析
  • 原文地址:https://www.cnblogs.com/lsfv/p/5575442.html
Copyright © 2011-2022 走看看