今天为了测试data member pointer,在BCB6里写了一段程序,却发现了一点问题。
代码很简单:
struct Base1 { int val1;};
printf("&Base1::val1 = %p, %d\n", &Base1::val1, 2);
结果发现数字2 输出失败,显示为0,察看CPU发现:
0040116B 6A02 push 0x02
0040116D FF35A8204000 push dword ptr [0x4020A8]
00401173 FF35A4204000 push dword ptr [0x4020A4]
00401179 68D4204000 push 0x004020d4
0040117E E831020000 call CC3260MT._printf
00401183 83C410 add esp,0x10
原来第一个参数“两次”被push入栈,导致printf认为第一次push的就是%d的参数。将上面的程序改为:
printf("&Base1::val1 = %p, %d %d\n", &Base1::val1, 2);
后可以发现2的输出。
为什么会将pointer入栈两次?
----------------------------------------------------
1/9/2006通过一个简单的测试:
int Base1::*pp;
printf("%d\n",sizeof(pp));
发现在BCB中是8字节(即使将Data Alignment设置成word或者double word也没有区别),而在VC或者GNU C++中是4字节。
谢谢某鸟的解释,于是查了一下__closure。让人惊讶的是他可以让一个“普通”的函数指针指向“thiscall”调用的成员函数(Using a closure, you can get a pointer to member function for an object (i.e. a particular instance of a class). The object can be any object, regardless of its inheritance hierarchy. The object抯 this pointer is automatically used when calling the member function through the closure. )。看来多出来的4字节是用来记录this指针了。(结果也很好证实)
不过:
[问题一]:
为什么data member pointer也会多这4字节?用来做什么?还是只是单纯的为了统一?
试了几个case,包括虚拟继承,发现“后面”的4字节都是0。本来以为是记录继承类中基类的偏移量,发现不是。又以为和上面一样与this指针有关系,不过,data member pointer用this指针做什么?后来猜想为了决议二义性,但因为下面的问题,测试未遂。
[问题二]:
本来想做另外一个实验:
struct Base1 { int val1; };
struct Base2: Base1 { int val2;};
struct Base3: Base1 { int val3;};
struct Derived : Base2, Base3 {int val4;};
如果定义一个Derived d; 显然可以通过d.Base2::val1和d.Base3::val1来分别访问两个val1,但如果是data member pointer呢?如何解决二义性,用一个int Base1::*pp;如何分别访问两个val1?
我试了
pp = &Derived::Base2::val1;
pp = &Derived::Base2.val1;
pp = &Derived.Base2::val1;
pp = &Derived.Base2.val1;
都不行。
----------------------------------------------------
1/13/2005
本来只想把问题局限到BCB中,不过既然不停的提到了VC中data member pointer。我也说说自己的看法。
一如ICOM(《Incide The C++ Object Model》,《深入探索C++对象模型》)中说,一般data member pointer用offset+1,这样可以避免以下的混乱。
int Base1::*p1 = 0;
int Base1::*p2 = &Base1::val1;
但是在VC中直接用的是offset。VC中避免这种的混乱的方法是int Base1::*p1 = 0;这样语句,实际上把p1置成了0xFFFFFFFFH,而非像普通pointer一样的0。判断
if (p1 == 0)
被转换成为 cmp dword ptr[...], 0FFFFFFFFh
相当于说,BCB/GNU data member pointer的值 = VC data member pointer的值 + 1