虚函数初探

今天读了coolshell博主几年前的旧文,C++虚函数表解析,代码如下:

#pragma once

#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;}
private: 
    virtual void p(){cout<<"Base::p(private)"<<endl;}
};

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 Derive:public Base1,public Base2,public Base3
{
public:
    virtual void f(){ cout<<"Derive::f()"<<endl;}
    virtual void g1(){cout<<"Derive::g1()"<<endl;}
};

typedef void (*Func)(void);

int main()
{
    Base b;
    Func pFun = NULL;
    cout<<"虚函数表的地址:"<<(int*)(&b)<<" "<<(void*)(&b)<<endl;
    cout<<"虚函数表中第一个函数的位置:"<<(int*)*reinterpret_cast<int*>(&b)<<
        " "<<(int*)*(int*)(&b)<<endl;
    pFun = (Func)*((int*)*(int*)(&b)); //Base::f()
    pFun();
    pFun = (Func)*((int*)*(int*)(&b) + 1);
    pFun();
    pFun = (Func)*((int*)*(int*)(&b) + 2);
    pFun();
    pFun = (Func)*((int*)*(int*)(&b) + 3); //通过虚函数表调用父类private虚函数
    pFun();

    Derive d;
    int** pVtab = (int**)&d;
    //Base1's vtable
    //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0);
    pFun = (Func)pVtab[0][0];
    pFun();

    //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1);
    pFun = (Func)pVtab[0][1];
    pFun();

    //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2);
    pFun = (Func)pVtab[0][2];
    pFun();

    //Derive's vtable
    //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3);
    pFun = (Func)pVtab[0][3];
    pFun();

    //The tail of the vtable
    pFun = (Func)pVtab[0][4];
    cout<<pFun<<endl;


    //Base2's vtable
    //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);
    pFun = (Func)pVtab[1][0];
    pFun();

    //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);
    pFun = (Func)pVtab[1][1];
    pFun();

    pFun = (Func)pVtab[1][2];
    pFun();

    //The tail of the vtable
    pFun = (Func)pVtab[1][3];
    cout<<pFun<<endl;



    //Base3's vtable
    //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);
    pFun = (Func)pVtab[2][0];
    pFun();

    //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);
    pFun = (Func)pVtab[2][1];
    pFun();

    pFun = (Func)pVtab[2][2];
    pFun();

    //The tail of the vtable
    pFun = (Func)pVtab[2][3];
    cout<<pFun<<endl;
    system("pause");
    return 0;
}

下面是一点感悟:

1.为什么只能通过强行把&b转成int *,取得虚函数表的地址。转换成(char*)或者(void*)就没法正确获取地址?

比如这样:(int*)*(char*)(&b) 或者(char*)*(char*)(&b)。(void*)*(int*)(&b),(char*)*(int*)(&b)可获得正确的结果。

突然意识到这也算问题?在32位的编译器设置下指针占用的地址为4byte和int类型一样,*(T*)(&b)只要获取一个产度为4byte的值作为函数指针的地址即可,也就是说T的长度需要和指针产度一致才能获取正确的地址,例如:(long*)*(long*)(&b),也可获取虚函数表的地址。

2.虚函数表的结构:

i.一般继承(无虚函数覆盖):虚函数表类似一个一维数组的链式结构

表中存的是虚函数的地址:

获取虚函数表的地址:(int*)(&b)

获取虚函数表中第一个函数的地址:(int*)*(虚函数表的地址 + 0)

第二个函数的地址:(int*)*(虚函数表的地址 + 1)

ii.多重继承的时候虚函数表的结构类似如一个二维数组,每一行对应一个基类的虚函数序列:

int** pVtab = (int**)(&derive);

原文地址:https://www.cnblogs.com/fripside/p/2952707.html