虚函数作为C++的核心,常见的问题有:
1.虚函数和普通成员函数的区别是什么;
2.虚函数表属于类还是对象
有虚函数的单继承
下面,我们假设有如下所示的单继承关系:
在这个继承关系中,父类,子类,都有自己的成员变量。父类有3个虚函数,分别是f(),g(),h()。子类重写了父类的f()和g()函数,子类同时新增了虚函数h1()。
源代码如下:
class Base
{
public:
Base(int iBase) : iBase(iBase){}
virtual void f(){ cout<<"Base::f()"<<endl; }
virtual void g(){ cout<<"Base::g()"<<endl; }
virtual void h(){ cout<<"Base::h()"<<endl; }
private:
int iBase;
};
class Derive : public Base
{
public:
Derive(int iBase, int iDerive) : Base(iBase), iDerive(iDerive) {}
virtual void f() { cout<<"Derive::f()"<<endl; }
virtual void g() { cout<<"Derive::g()"<<endl; }
virtual void h1(){ cout<<"Derive::h1()"<<endl; }
private:
int iDerive;
};
我们通过下面这个小程序来查看子类实例的内存布局:下面程序中,我使用了一个指向指针的指针变量pVtable指向子类的地址,后面的测试程序也这样来进行测试。
typedef void (*Func)();
Derive d(10, 100);
int **pVtable = (int **)&d;
Func pFun = NULL;
cout << " [0] Base::vftable" << endl;
pFun = (Func)pVtable[0][0];
cout << " [0] ";
pFun();
pFun = (Func)pVtable[0][1];
cout << " [1] ";
pFun();
pFun = (Func)pVtable[0][2];
cout << " [2] ";
pFun();
pFun = (Func)pVtable[0][3];
cout << " [3] ";
pFun();
pFun = (Func)pVtable[0][4];
cout << " [4] "<<pFun<<endl;
cout << " [1] Base::iBase = "<<(int)pVtable[1]<<endl;
cout << " [2] Derive::iDerive = "<<(int)pVtable[2]<<endl;
运行界面如下:
父类和子类在内存中的布局如下:
1>class Base size(8):
1> +---
1> 0 | {vfptr}
1> 4 | iBase
1> +---
1>Base::$vftable@:
1> | &Base_meta
1> | 0
1> 0 | &Base::f
1> 1 | &Base::g
1> 2 | &Base::h
1>class Derive size(12):
1> +---
1> | +--- (base class Base)
1> 0 | | {vfptr}
1> 4 | | iBase
1> | +---
1> 8 | iDerive
1> +---
1>Derive::$vftable@:
1> | &Derive_meta
1> | 0
1> 0 | &Derive::f
1> 1 | &Derive::g
1> 2 | &Base::h
1> 3 | &Derive::h1
结论
1、虚函数表在类的最开始位置
2、子类覆盖父类的虚函数在虚函数表得到体现
3、子类新增的虚函数添加到虚函数表的末尾
虚函数表是在对象中
有虚函数的多继承
下面,我们假设有如下所示的多重继承关系:
在这个继承关系中,父类,子类,都有自己的成员变量。而子类多重继承父类Base1和Base2以及Base3,都分别重写他们的一个虚函数,并且新增了两个虚函数derive_f1()和derive_g1()。
源代码:
class Base1
{
public:
Base1(int iBase1) : iBase1(iBase1){}
virtual void f1(){ cout<<"Base1::f1()"<<endl; }
virtual void g1(){ cout<<"Base1::g1()"<<endl; }
virtual void h1(){ cout<<"Base1::h1()"<<endl; }
private:
int iBase1;
};
class Base2
{
public:
Base2(int iBase2) : iBase2(iBase2){}
virtual void f2(){ cout<<"Base2::f2()"<<endl; }
virtual void g2(){ cout<<"Base2::g2()"<<endl; }
virtual void h2(){ cout<<"Base2::h2()"<<endl; }
private:
int iBase2;
};
class Base3
{
public:
Base3(int iBase3) : iBase3(iBase3){}
virtual void f3(){ cout<<"Base3::f3()"<<endl; }
virtual void g3(){ cout<<"Base3::g3()"<<endl; }
virtual void h3(){ cout<<"Base3::h3()"<<endl; }
private:
int iBase3;
};
class Derive : public Base1, public Base2, public Base3
{
public:
Derive(int iBase1, int iBase2, int iBase3, int iDerive) : Base1(iBase1), Base2(iBase2), Base3(iBase3), iDerive(iDerive) {}
virtual void f1() { cout<<"Derive::f()"<<endl; }
virtual void g2() { cout<<"Derive::g()"<<endl; }
virtual void h3() { cout<<"Derive::h1()"<<endl; }
virtual void derive_f1(){ cout<<"Derive::derive_f1()"<<endl; }
virtual void derive_g1(){ cout<<"Derive::derive_g1()"<<endl; }
private:
int iDerive;
};
我们用下面代码来测试子类Derive的布局情况:
typedef void (*Func)();
Derive d(10, 20 ,30, 100);
int **pVtable = (int **)&d;
Func pFun = NULL;
cout << " [0] Base1::vftable" << endl;
pFun = (Func)pVtable[0][0];
cout << " [0] ";
pFun();
pFun = (Func)pVtable[0][1];
cout << " [1] ";
pFun();
pFun = (Func)pVtable[0][2];
cout << " [2] ";
pFun();
pFun = (Func)pVtable[0][3];
cout << " [3] ";
pFun();
pFun = (Func)pVtable[0][4];
cout << " [4] ";
pFun();
pFun = (Func)pVtable[0][5];
cout << " [5] "<<pFun<<endl;
cout << " [1] Base1::iBase1 = " <<(int)pVtable[1]<<endl;
int pos = sizeof(Base1)/4;
cout << " ["<<pos<<"] Base2::vftable" << endl;
pFun = (Func)pVtable[pos][0];
cout << " [0] ";
pFun();
pFun = (Func)pVtable[pos][1];
cout << " [1] ";
pFun();
pFun = (Func)pVtable[pos][2];
cout << " [2] ";
pFun();
pFun = (Func)pVtable[0][3];
cout << " [3] "<<pFun<<endl;
cout << " ["<<pos + 1<<"] Base2::iBase2 = " <<(int)pVtable[pos + 1]<<endl;
pos += sizeof(Base2)/4;
cout << " ["<<pos<<"] Base3::vftable" << endl;
pFun = (Func)pVtable[pos][0];
cout << " [0] ";
pFun();
pFun = (Func)pVtable[pos][1];
cout << " [1] ";
pFun();
pFun = (Func)pVtable[pos][2];
cout << " [2] ";
pFun();
pFun = (Func)pVtable[0][3];
cout << " [3] "<<pFun<<endl;
pos++;
cout << " ["<<pos<<"] Base3::iBase3 = " <<(int)pVtable[pos]<<endl;
pos++;
cout << " ["<<pos<<"] Derive::iDerive = " <<(int)pVtable[pos]<<endl;
运行界面如下:
VC输出窗口输出的子类内存的布局信息如下:
1>class Derive size(28):
1> +---
1> | +--- (base class Base1)
1> 0 | | {vfptr}
1> 4 | | iBase1
1> | +---
1> | +--- (base class Base2)
1> 8 | | {vfptr}
1>12 | | iBase2
1> | +---
1> | +--- (base class Base3)
1>16 | | {vfptr}
1>20 | | iBase3
1> | +---
1>24 | iDerive
1> +---
1>Derive::$vftable@Base1@:
1> | &Derive_meta
1> | 0
1> 0 | &Derive::f1
1> 1 | &Base1::g1
1> 2 | &Base1::h1
1> 3 | &Derive::derive_f1
1> 4 | &Derive::derive_g1
1>Derive::$vftable@Base2@:
1> | -8
1> 0 | &Base2::f2
1> 1 | &Derive::g2
1> 2 | &Base2::h2
1>Derive::$vftable@Base3@:
1> | -16
1> 0 | &Base3::f3
1> 1 | &Base3::g3
1> 2 | &Derive::h3
结论:
1、在子类中的每一个父类都有一个虚函数表
2、子类新增的虚函数,按继承顺序放在第一个父类的虚函数表中
3、子类覆盖父类的虚函数在每一个父类的虚函数表得到体现