Author:Jeff 2005-12-2
关键字:C++ 虚函数 栈 存储
环境:Window XP Professional + SP2, VC6.0
无论在栈中还是在堆中申请内存空间,项目组都会要求用memset()将申请到的空间清0。对于简单数据类型数组和结构简单的struct,class,memset()很好用。但是对于某些struct或者class,memset()之后程序会莫名其妙down掉,排起错误来很费力。最后发现用memset()清0的struct或者class,要么里面有虚函数,要么包含的成员对象有虚函数;memset()将空间清0,间接地将指向virtual table的指针清空。如果程序不down掉,那才奇怪呢!
下面看看class在栈中的存储情况。
class C001
{
public:
C001() : ch(32), pos(1) { };
virtual ~C001() { };
private:
virtual void Display(void) {
printf("display C002");
}
public:
virtual void Outp(int n) {
printf("Number = %d/n" , n);
}
private:
int pos;
char ch;
};
int main(void)
{
int d = 0x1234;
C001 c1;
return 0;
}
(代码写的不好看,将就)
编译,Debug,看看变量在内存的存储情况。class内部成员变量按定义的先后顺序由低地址向高地址方向存储(与栈的增长方向相反)。而函数内部变量存储方向与class的相反,与栈的增长方向相同。
int *p = (int *)&c1;
printf("*(p + 1) = %d/n", *(p + 1)); // value of pos
printf("*(p + 2) = %d/n", (*(p + 2) & 0xff)); // value of ch
printf("*(p + 3) = 0x%08x/n", *(p + 3)); // value of d
在class的第一个变量的前面存放不是CCCCCCCC(Debug版本),而是某个具体的值。这个值就是今后研究的重点,指向virtual table指针。class在栈中的情况大致如下:
(我的机器上virtual table位于常量存储区,有点怪怪的,不知道为什么?)
virtual table的第一个元素是C001::`scalar deleting destructor'(不知道如何称呼它)。在C001::`scalar deleting destructor'函数体内先调用C001::~C001(),再调用operator delete释放申请的内存空间。 在virtual table的最后是00 00 00 00,表示virtual table的结束。有了这些,就可以计算class到底有多少个虚函数(包括继承来的)。
int *p = (int *)&c1;
int *q;
for (int i = 0; ; i++) {
q = (int *)*((int *)(*p) + i);
if (NULL == q) {
printf("virtual function number = %d/n", i);
break;
}
}
对class C001来说,virtual function number = 3。