美文网首页
c++虚函数与虚表初步

c++虚函数与虚表初步

作者: FakeCSer爱去网吧 | 来源:发表于2020-04-24 18:13 被阅读0次

    虚指针与虚表

    • 虚表和虚函数是为了实现动态多态的机制,由编译器实现

    • 当一个类本身定义了虚函数,或其父类有虚函数时,编译器为该创建一个虚函数表,表中按函数定义的顺序存放指向虚函数的指针。

    • 带虚函数的类实例化为对象的时候,在堆栈上给对象的开始位置加一个虚函数指针,(放在开始是为了保证在多层继承或多重继承的情况下能以最高)

    • 函数表vtable在Linux/Unix中存放在可执行文件的只读数据段中(rodata),这与微软的编译器将虚函数表存放在常量段存在一些差别

    • 虚函数表是class specific的,也就是针对一个类来说的,这里有点像一个类里面的staic成员变量,即它是属于一个类所有对象的,不是属于某一个对象特有的,是一个类所有对象共有的。

    代码:通过函数指针访问类对象中的虚函数表,来调用虚表中的虚函数
    • 对象的地址就是存放虚指针的地址,long * vptrAddress = (long *)(&a);
    • 虚指针指向虚表,long* vfunAddress = (long*)*vptrAddress;
    • 虚表中存放虚函数的地址,用函数指针取函数,vfunc = (F)*(long*)vfunAddress;
    #include <iostream>
    using namespace std;
    class A
    {
    public:
        virtual void Fun(){cout<<"this is A::Fun()"<<endl;}
        virtual void Fun1(){cout<<"this is A::Fun1()"<<endl;}
    };
    
    typedef void (*F)();
    F vfunc;//定义和声明函数指针
    
    int main()
    {
        A a;
    //获取对象首地址(即虚表指针)
        long * vptrAddress = (long *)(&a);
        cout << vptrAddress<<endl;
    //获取虚表的首地址
        long* vfunAddress = (long*)*vptrAddress;
        cout << vfunAddress<<endl;
    
    //用函数指针直接调用地址的方法调用类中的函数
    //  vfunc = (F)*((long*)*(long*)(&a));
        vfunc = (F)*(long*)vfunAddress; 
        vfunc();
    //指针指向虚表下一个函数的地址,赋给函数指针来进行调用
        vfunc = (F)*((long*)vfunAddress+1);
        vfunc();
        return 0;
    }
    
    

    注意地址要用long *来存,不然存不下,指向不知道什么地方造成段错误
    因为64位机器上,指针是8字节的(64/8),所以用long,在32位机器上,地址是4字节
    运行结果:



    又学到一个牛逼的方法
    long** pVtab= (long**)&a;来操作
    此时pVtab[0]就是虚函数表地址,pVtab[1]依次往下就是成员变量,注意内存对齐,如果成员有连续两个int,取到的就是一个不想要的值

    #include <iostream>
    using namespace std;
    class A
    {
    public:
        virtual void Fun(){cout<<"this is A::Fun()"<<endl;}
        virtual void Fun1(){cout<<"this is A::Fun1()"<<endl;}
        A(int MEM,int MEM1)
            :mem(MEM),mem1(MEM1){}  
    public:
        int mem;
        long mem1;
    };
    
    typedef void (*F)(void);
    F vfunc;//定义和声明函数指针
    A a(10,20);
    
    long** pVtab = (long**)&a;
    
    int main()
    {
        vfunc = (F)pVtab[0][0];
        vfunc();
        
        vfunc = (F)pVtab[0][1];
        vfunc();
    //access mem
        cout << "mem="<<(long)pVtab[1]<<endl;
        cout << "mem1="<<(long)pVtab[2]<<endl;
        
        return 0;
    }
    

    今天学习this指针的时候又在原程序上加了些功能

    #include <iostream>
    using namespace std;
    class A
    {
        public:
            virtual void Fun(){cout<<"this is A::Fun()"<<endl;}
            virtual void Fun1(){cout<<"this is A::Fun1()"<<endl;}
            A(int MEM,int MEM1)
                :mem(MEM),mem1(MEM1){}  
            void showThis(){cout<<"this pointer: " << "\t"<< hex << this << endl;}
        public:
            int mem;
            long mem1;
    };
    
    typedef void (*F)(void);
    F vfunc;//定义和声明函数指针
    A a(10,20);
    
    long** pVtab = (long**)&a;
    
    int main()
    {
        //access function member
        vfunc = (F)pVtab[0][0];
        vfunc();
    
        vfunc = (F)pVtab[0][1];
        vfunc();
        //access data member
        cout << "mem="<< (long)pVtab[1] << endl;
        cout << "mem1="<< (long)pVtab[2] << endl;
        cout << "addr of vp: " << "\t" << hex << &pVtab[0] << endl; 
        cout << "addr of a: " << "\t" << hex << &a << endl;
        a.showThis();
        return 0;
    }
    

    可见,this指针指向的地址==对象的首地址(对象的位置)==虚指针的位置

    相关文章

      网友评论

          本文标题:c++虚函数与虚表初步

          本文链接:https://www.haomeiwen.com/subject/xqsxwhtx.html