本文重点参考了《C++ 虚函数表解析》一文(链接:),陈皓前辈此文讲解清晰,之前阅读受益匪浅,只是代码中存在一些问题。例如涉及到本文重点虚函数表的地方,写到

Base b;cout << "虚函数表地址:" << (int*)(&b) << endl;

    但是,实际上(int*)(&b)并非虚函数表地址,而是对象b的地址,*(int*)(&b)才是虚函数表的地址。此外,后文中一些指针操作也有异常之处(也有可能是编译环境不同?)。为此,我重新进行编码实验,并记录此文。

一、实验环境

本机操作系统Linux wm-ThinkPad-X240s 3.13.0-44-generic #73-Ubuntu SMP Tue Dec 16 00:22:43UTC 2014 x86_64 x86_64 x86_64 GNU/Linux。注意是64位机器,意味着指针长度为8字节;如果是32位机器,则指针长度为4字节。

    我们根据包含虚函数的类是否涉及继承、如何继承,分单个类无继承、一般继承、多重继承三种情况,讨论虚函数的内存分配。

二、单个类无继承

        我们编写下列代码,其中类Base包含三个虚函数。

#include 
 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; }}; typedef void (*Fun) (); int main() {    Base b;    Fun pFun = NULL;     cout << "sizeof(b): " << sizeof(b) << endl;     cout << "对象的地址: " << (&b) << endl;    cout << "虚函数表的地址: " << *(int*)(&b) << endl;     pFun = (Fun)(*(int*)(*(int*)(&b)));    pFun();     pFun = (Fun)(*(int*)(*(int*)(&b) + 8));    pFun();     pFun = (Fun)(*(int*)(*(int*)(&b) + 16));    pFun();     return 0;}

运行结果为

根据运行结果可知,对象b的内存规模为8字节,这8字节为存储虚函数表指针所占用(64位机器,指针8字节)。

这里顺便说一些C++类中与虚函数无直接关系的内存分配知识,如果没有虚函数,则类和对象的所占内存为类中数据成员的内存量(需要考虑对齐),类中函数成员不占内存量。那么如果一个类没有数据成员和虚函数,对其求sizeof,结果应当是多少呢?结果本来应当是0,但是一个实例它必须在内存中占有一定的空间,因此实际结果为1。《剑指offer》中就有类似的题目,有兴趣的朋友可以自行实验。

根据代码及其运行结果,我们可以推断出其内存分配如下图所示。

三、一般继承

        我们编写下列代码,其中Base为基类, Derive继承Base类。

#include 
 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; }}; class Derive : public Base {public:    void f() { cout << "Derive::f" << endl; }    virtual void g1() { cout << "Derive::g1" << endl; }    virtual void h1() { cout << "Derive::h1" << endl; }}; typedef void (*Fun) (); int main() {    Derive d;    Fun pFun = NULL;     cout << "sizeof(d): " << sizeof(d) << endl;    cout << "对象的地址: " << (&d) << endl;    cout << "虚函数表的地址: " << *(int*)(&d) << endl;     pFun = (Fun)(*(int*)(*(int*)(&d)));    pFun();     pFun = (Fun)(*(int*)(*(int*)(&d) + 8));    pFun();     pFun = (Fun)(*(int*)(*(int*)(&d) + 16));    pFun();     pFun = (Fun)(*(int*)(*(int*)(&d) + 24));    pFun();     pFun = (Fun)(*(int*)(*(int*)(&d) + 32));    pFun();     return 0;}

运行结果为

根据代码及其运行结果,我们可以推断出其内存分配如下图所示。

四、多重继承

        我们编写下列代码,类Derive继承类Base1Base2Base3

#include 
 using namespace std; 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:    void f() { cout << "Derive::f" << endl; }    virtual void g1() { cout << "Derive::g1" << endl; }    virtual void h1() { cout << "Derive::h1" << endl; }}; typedef void(*Fun)(void); int main() {    Derive d;    Fun pFun = NULL;    int** pVtab = (int**)&d;     cout << "sizeof(b): " << sizeof(d) << endl;     pFun = (Fun)pVtab[0][0];    pFun();     pFun = (Fun)pVtab[0][2];    pFun();     pFun = (Fun)pVtab[0][4];    pFun();     pFun = (Fun)pVtab[0][6];    pFun();     pFun = (Fun)pVtab[0][8];    pFun();     pFun = (Fun)pVtab[1][0];    pFun();     pFun = (Fun)pVtab[1][2];    pFun();     pFun = (Fun)pVtab[1][4];    pFun();     pFun = (Fun)pVtab[2][0];    pFun();     pFun = (Fun)pVtab[2][2];    pFun();     pFun = (Fun)pVtab[2][4];    pFun();     return 0;}

运行结果

根据代码及其运行结果,我们可以推断出其内存分配如下图所示。