C++-指向类成员变量的指针
#include <iostream>
using namespace std;
class Base
{
public:
Base(){}
public:
int value_;
};
int main()
{
int Base::*ptrToMember = NULL;
printf("the ptrToMember is 0x%pn", ptrToMember);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
剑灵分析的有一定的道理,我也认为是编译做了一些处理。为什么这样,我觉得一种情况是:
当ptrToMember=0时,是有其明确意义的,此时它指向的是第一个成员value,这样就可以确保程序员能够明确地将ptrToMember指向正确的成员。
int Base::*ptrToMember = NULL;
我将代码改成:
int Base::*ptrToMember = 0;
结果没有大的区别,编译还是会将-1赋值给了ptrToMember。
为了继续印证“指向成员变量的指针保存的是偏移量”这个说法,我对源代码做了些修改,以便于调试。
#include <iostream>
using namespace std;
class Base
{
public:
Base(): value(10), value2(100) {}
void memberFunc() { cout << "just a testn"; }
public:
int value;
int value2;
};
int main()
{
Base b;
int i;
int Base::*ptrToMember = NULL;
printf("the ptrToMember is 0x%pn", ptrToMember);
i = b.*ptrToMember;
cout << "i=" << i << endl;
ptrToMember = &Base::value;
printf("the ptrToMember is 0x%pn", ptrToMember);
i = b.*ptrToMember;
cout << "i=" << i << endl;
ptrToMember = &Base::value2;
printf("the ptrToMember is 0x%pn", ptrToMember);
void (Base::*ptrToFunction)() = NULL;
printf("the ptrToFunction is 0x%pn", ptrToFunction);
ptrToFunction = &Base::memberFunc;
printf("the ptrToFunction is 0x%pn", ptrToFunction);
}
输出的结果如下:
the ptrToMember is 0xFFFFFFFF
i=2764
the ptrToMember is 0x00000000
i=10
the ptrToMember is 0x00000004
the ptrToFunction is 0x00000000
the ptrToFunction is 0x011F1032
在看看汇编代码:
; 20 : int Base::*ptrToMember = NULL;
mov DWORD PTR _ptrToMember$[ebp], -1
; 22 : i = b.*ptrToMember;
mov eax, DWORD PTR _ptrToMember$[ebp]
mov ecx, DWORD PTR _b$[ebp+eax]
mov DWORD PTR _i$[ebp], ecx
第一次i的值为2764对应的16进制的值为0xacc,为何是2764而不是其他的值?继续跟进,查看内存分布的情况:
对象b的地址为:0x003cfaf8。
则:
mov ecx, DWORD PTR _b$[ebp+eax] ;eax=-1
那么ecx的值为0x00000acc(注意这里是小端模式),同时也印证指向类成员指针确实是相对于某一个对象地址的偏移量。
指向成员变量的指针里面存的是成员在对象的内存布局中的相对偏移地址
指向成员函数的指针里面存的是函数的实际地址
int Base::*ptrToMember = NULL; 所以编译器会把ptrToMember设为-1,作为区分。
指向类成员的指针里面存储的是一个偏移量而不是绝对地址。
举个例子:
class C
{
public:
int value;
int value2;
int value3;
C(int v=0): value(v) {};
void f() {}
};
C getC(int v)
{
C c1;
return c1;
}
int main()
{
return 0;
}
使用VS查看内存布局的命令:
cl [filename].cpp /d1reportSingleClassLayout[className]
查看类C的内存布局:
可以看到编译器是用偏移量来表示类成员的位置的。通过对象的地址和这个偏移量的计算就能得出,成员的具体地址。而偏移量是从0开始的。
int Base::*ptrToMember = NULL; 所以编译器会把ptrToMember设为-1,作为区分。
大约编译器就是这么规定的吧。其实指向类成员的指针里面存储的是一个偏移量而不是绝对地址,因此可以使用指针来访问:
someObj->(*ptrToMember)
实际上是把指针地址加上偏移量,然后取地址中的值。
但是偏移量完全可能是0,比如说你这个例子里面,指向value_的情况下偏移量就是0。为了不和0偏移量混淆,只好规定逻辑上指针为空的时候,实际上存的值是-1(因为偏移量不可能是负数)了吧。