虚表剖析

来源:互联网 发布:lstm python 编辑:程序博客网 时间:2024/04/29 01:10

虚表剖析
举例:

   class Test{public:Test(){    _test = 10;    cout << "this = " << this << endl;};virtual ~Test(){}private:int _test;};    int main(){Test t;cout << sizeof(Test) << endl;system("pause");return 0;};

这里写图片描述
当有虚函数时,编译器会生成一张虚表,在对象的前四个字节(虚表指针);
当有多个虚函数时,函数模型又是怎样呢?
上代码验证(没有重写):
在基类和派生类中分别添加虚函数

class CBase{public:CBase(){m_iTest = 10;}virtual void FunTest0(){cout<<"CBase::FunTest0()";}virtual void FunTest1(){cout<<"CBase::FunTest1()";}virtual void FunTest2(){cout<<"CBase::FunTest2()";}private:int m_iTest;};class CDerived:public CBase{public:virtual void FunTest4() {cout<<"CDerived::FunTest4()";}    virtual void FunTest5(){cout<<"CDerived::FunTest5()";}virtual void FunTest6(){cout<<"CDerived::FunTest6()";}};typedef void (*FUN_TEST)();void FunTest(){CBase base;cout<< "CBase vfptr:"<<endl;for (int iIdx = 0; iIdx < 3; ++iIdx){FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&base + iIdx));funTest();cout<< ": "<<(int *)funTest<<endl;    }cout<<endl;CDerived derived;cout<< "CDerived vfptr:"<<endl;for (int iIdx = 0; iIdx < 6; ++iIdx){    FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&derived + iIdx));funTest();cout<< ": "<<(int *)funTest<<endl;}}

运行结果:
Alt text

结论:
1.虚函数按照声明顺序存放在虚表中,
2.在派生类中,先是基类的虚函数,后存放派生类的虚函数,

有重写

class CBase{public:virtual void FunTest0(){cout<<"CBase::FunTest0()"<<endl;}virtual void FunTest1(){cout<<"CBase::FunTest1()"<<endl;}virtual void FunTest2(){cout<<"CBase::FunTest2()"<<endl;}virtual void FunTest3(){cout<<"CBase::FunTest3()"<<endl;}};class CDerived:public CBase    {public:virtual void FunTest0(){cout<<"CDerived::FunTest0()" <<endl;}virtual void FunTest1(){cout<<"CDerived::FunTest1()" <<endl;}virtual void FunTest4(){cout<<"CDerived::FunTest4()" <<endl;}virtual void FunTest5(){cout<<"CDerived::FunTest5()" <<endl;}};

这里写图片描述

1.在派生类中,先拷贝基类的虚函数表,如果重写了某个虚函数,就在对应位置换成重写后的虚函数,
2.接着跟上自己定义的虚函数,

ATTENTION:

CBase base;CDerived derived;pBase = &base;//指向基类,得到的是基类的虚表指针pBase->FunTest();pBase = &derived;//指向派生类,得到派生类的指针pBase->FunTest();CDerived* ptr = (CDerived*)&base;//派生类的指针指向基类地址ptr->FunTest();

通过基类的引用或指针调用虚函数时,要根据运行时指向的类型决定;
调用非虚函数时,无论基类指向何种类型,调用的都是基类的函数,

通过一个派生类类型的指针强制指向基类地址,调用非虚函数时是派生类的函数,调用虚函数时是调用基类中的。

多重继承:
上代码:

class CBase0{public:CBase0(){m_iTest = 0xB0;}virtual void PrintB0(){cout<<"m_iTest = "<<hex<<m_iTest<< " CBase0::PrintB0()" <<endl;}int m_iTest;};class CBase1{public:CBase1(){m_iTest = 0xB1;}virtual void PrintB1(){cout<<"m_iTest = "<<hex<<m_iTest<< " CBase1::PrintB1()" <<endl;}int m_iTest;};class CBase2{public:CBase2(){m_iTest = 0xB2;}virtual void PrintB2(){cout<<"m_iTest = "<<hex<<m_iTest<< " CBase2::PrintB2()" <<endl;}int m_iTest;};class CDerived:public CBase0, public CBase1, public CBase2{public:CDerived(){m_iTest = 0xD0;}virtual void PrintD(){cout<<"m_iTest = "<<hex<<m_iTest<< " CDerived::PrintD()"<<endl;}int m_iTest;};

数据模型:
这里写图片描述

CDerived 的虚函数表放在第一个继承基类的虚表后面;

当有虚函数覆盖时,每次调用的都是派生类中重写的;

0 0