03B35EA8 6CC5849C dChkData.6CC5849C
03B35EAC 003A0043
03B35EB0 0050005C
03B35EB4 006F0072
这儿先来看看虚函数表的指针的内存布局,具体看下例
#include <stdio.h>
class simpleClass{
public:
static int nCount;
int nValue;
char c;
simpleClass(){};
virtual ~simpleClass(){};
int getValue(void);
virtual void foo(void){};
static void addCount();
};
int main()
{
simpleClass aSimple;
printf("Object start address: %x\n",&aSimple);
printf("nValue address: %x\n",&aSimple.nValue );
printf("c address: %x\n",&aSimple.c );
printf("size: %d\n",sizeof(simpleClass));
return 0;
}
所得结果如图所示:
如是乎一个简单的C++对象的内存布局展示出来了:
由以上分析可以证明以下结论:
虚函数表的指针存在于对象实例中最前面的位置(我使用的是VC6,至于其他的编译器嘛,个人能力有限,有待以后考查)。
【除此之外还包含有对其他许多知识点的验证:
1 非静态数据成员是影响对象占据内存大小的主要因素,随着对象数目的增加,非静态数据成员占据的内存会相应增加。
2 所有的对象共享一份静态数据成员,所以静态数据成员占据的内存的数量不会随着对象数目的增加而增长。
3 静态数据成员和非静态数据成员不会影响对象内存的大小,虽然其实现会占据相应的内存空间,同样也不会随着对象数目的增加而增加。】
还有一点需要补充的是:所有的虚函数共享同一张虚函数表。
对于子类实例中的虚函数表,是下面这个样子:
我们可以看到:
1) 每个父类都有自己的虚表。
2) 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)
假设我们有这样的一个类:
class Base {
public:
virtualvoid f() { cout << "Base::f" << endl; }
virtualvoid g() { cout << "Base::g" << endl; }
virtualvoid h() { cout << "Base::h" << endl; }
};
对于实例:Derive d; 的虚函数表如下:
我们可以看到下面几点:
1)虚函数按照其声明顺序放于表中。
2)父类的虚函数在子类的虚函数前面。
因为在实际的应用中没有人会如此无聊的去定义这样的派生类Derive,所以读者可以自己尝试的写写验证程序,类似第一节中的pFun。