深入理解C++虚函数表

虚函数作为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、子类覆盖父类的虚函数在每一个父类的虚函数表得到体现

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值