虚表剖析
举例:
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;
}
}
运行结果:
结论:
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 的虚函数表放在第一个继承基类的虚表后面;
当有虚函数覆盖时,每次调用的都是派生类中重写的;