或许编译器的差异, 所得的结果也不同, 但某些地方还是可以借鉴的.
帖子讨论:
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.