C++类实例占用的内存结构,及虚函数表的一点探究

来源:互联网 发布:scala编程 第二版 pdf 编辑:程序博客网 时间:2024/05/12 11:36

或许编译器的差异, 所得的结果也不同, 但某些地方还是可以借鉴的.

帖子讨论:

http://topic.csdn.net/u/20110509/11/43917452-ae4d-4ba9-8c72-25e454e60e75.html?113761501

 

参考:

http://www.kuqin.com/language/20090314/39911.html

http://www.cse.wustl.edu/~mdeters/seminar/fall2005/mi.html

 

 

运行结果如下:

run D2CPocket::func1()

run DCPocket::func2()

run CPocket::func3()

run D2CPocket::func4()

 

在VC++ 6.0 和CodeBlock中用watch查看D2CPocket 的实例d2所占的内存, 大致存储顺序如下:

|--CPocket成员所占的内存

|--DCPocket成员所占的内存

|--D2CPocket成员所占的内存

即是这样的:

CPocket虚函数表

CPocket::ptr

CPocket::val

DCPocket:seq

D2CPocket::index

 

结论1:类实例所占内存结构是基类成员在前,派生类成员在后的顺序.

结论2:类实例中的开头4字节, 是一个指针(即虚函数表指针), 指向一个数组vtable[], 数组的每个元素是虚函数地址.

            所以, (类实例开始的四字节)虚函数指针用解操作2次, 才能得到虚函数的入口地址;


还发现了一个小问题, 代码中的三个类CPocket, DCPocket, D2CPocket都是没有"虚析构函数", 如果类CPocket, DCPocket加上虚析构,再次执行代码, 会发现一些有意思的小现象, 不同编译器表现不同, 大致是虚函数被"意外"的调用了, 

为什么呢? 用vc6的watch查看类实例d2的虚函数表, 发现~ vtable[0]的内容, 是虚析构函数的地址,,,

所以第一次调用

pf = (pFunc)(*p);

pf(); 的时候,调用的是派生类的虚析构函数,

 

结论3:如果派生类的基类中还有虚析构, 那么派生类实例的虚函数表开头是该类的析构函数.

*注,如果某类不会当做基类, 那么不必把该类的虚构声明为virtual.