本文重点参考了《C++ 虚函数表解析》一文(链接:http://blog.csdn.net/haoel/article/details/1948051/),陈皓前辈此文讲解清晰,读后受益匪浅。只是代码中存在一些问题,例如涉及到本文重点虚函数表的地方,写到
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 <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; } |
运行结果为
根据运行结果可知,对象b的内存规模为8字节,这8字节为存储虚函数表指针所占用(64位机器,指针8字节)。
这里顺便说一些C++类中与虚函数无直接关系的内存分配知识,如果没有虚函数,则类和对象的所占内存为类中数据成员的内存量(需要考虑对齐),类中函数成员不占内存量。那么如果一个类没有数据成员和虚函数,对其求sizeof,结果应当是多少呢?结果本来应当是0,但是一个实例它必须在内存中占有一定的空间,因此实际结果为1。《剑指offer》中就有类似的题目,有兴趣的朋友可以自行实验。
根据代码及其运行结果,我们可以推断出其内存分配如下图所示。
三、一般继承
我们编写下列代码,其中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; } |
运行结果为
根据代码及其运行结果,我们可以推断出其内存分配如下图所示。
四、多重继承
我们编写下列代码,类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; } |
运行结果
根据代码及其运行结果,我们可以推断出其内存分配如下图所示。
本文出自 “说话的白菜” 博客,谢绝转载!
原文地址:http://speakingbaicai.blog.51cto.com/5667326/1686404