本文重点参考了《C++ 虚函数表解析》一文(连接:http://blog.csdn.net/haoel/article/details/1948051/),陈皓前辈此文讲解清晰,以前阅读受益不浅,只是代码中存在一些问题。例如涉及到本文重点虚函数表的地方,写到ios
Base b; cout << "虚函数表地址:" << (int*)(&b) << endl; |
可是,实际上(int*)(&b)并不是虚函数表地址,而是对象b的地址,*(int*)(&b)才是虚函数表的地址。此外,后文中一些指针操做也有异常之处(也有多是编译环境不一样?)。为此,我从新进行编码实验,并记录此文。ide
1、实验环境函数
本机操做系统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字节。编码
咱们根据包含虚函数的类是否涉及继承、如何继承,分单个类无继承、通常继承、多重继承三种状况,讨论虚函数的内存分配。
spa
2、单个类无继承
操作系统
咱们编写下列代码,其中类Base包含三个虚函数。.net
#include <iostream> 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; } |
运行结果为3d
根据运行结果可知,对象b的内存规模为8字节,这8字节为存储虚函数表指针所占用(64位机器,指针8字节)。指针
这里顺便说一些C++类中与虚函数无直接关系的内存分配知识,若是没有虚函数,则类和对象的所占内存为类中数据成员的内存量(须要考虑对齐),类中函数成员不占内存量。那么若是一个类没有数据成员和虚函数,对其求sizeof,结果应当是多少呢?结果原本应当是0,可是一个实例它必须在内存中占有必定的空间,所以实际结果为1。《剑指offer》中就有相似的题目,有兴趣的朋友能够自行实验。xml
根据代码及其运行结果,咱们能够推断出其内存分配以下图所示。
3、通常继承
咱们编写下列代码,其中Base为基类, Derive继承Base类。
#include <iostream> 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; } |
运行结果为
根据代码及其运行结果,咱们能够推断出其内存分配以下图所示。
4、多重继承
咱们编写下列代码,类Derive继承类Base1、Base2、Base3。
#include <iostream> 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; } |
运行结果
根据代码及其运行结果,咱们能够推断出其内存分配以下图所示。