普通指针使用*来标示,而指向类的成员函数的指针使用ClassName::*标示。需要注意的是,指向类的数据成员的指针并非指针,因为它既不包含地址,行为也不像指针 。
与常规指针不同,一个指向成员的指针并不指向一个具体的内存位置,它指向的是一个类的特定成员,而不是指向一个特定对象里的特定成员。通常最清晰的做法是将指向数据成员的指针看作 为一个偏移量 。 C++标准并没有说该如何实现指向成员的指针,大多数编译器都将指向数据成员的指针实现为一个整数,其中包含被指向成员的偏移量。另外加上1(加1是为了让0值可以表示一个空的数据成员指针)。 这个偏移量告诉你,一个特定成员的位置距离对象的起点有多少个字节。一个类成员的偏移量在任何对象中都是相同的。
给定一个成员在类内的偏移量,为了访问位于那个偏移量的数据成员,我们需要该类的一个对象的地址。这时候就需要.*和->*这两个看上去非同寻常的操作符闪亮登场了。
//------
class C {
public:
//...
int a_;
};
int C::*pimC; // 一个指针,指向C的一个int成员
C aC;
C * pC = &aC;
pimC = &C::a_;
aC.* pimC = 0 ;
int b = pC - > *pimC;
//------
当写下pC->*pimC时,其实是请求将pC内的地址加上pimC内的偏移量,为的是访问pC所指向的C对象中适当的数据成员。当写aC.*pimC时,是在请求aC的地址加上pimC中的偏离量,也是为了访问pC所指向的C对象中适当的数据成员.
另外,存在从指向基类成员的指针到指向公有派生类成员的指针的隐式转换,但不存在从指向派生类成员的指针到指向其任何一个基类成员的指针的转换。这个可以从c++对象的内存布局来理解,因为基类成员一般在子类对象的前部分,所以这些成员在基类或者子类中的偏移是一样的;解析的时候,就是对象的首地址加上这个偏移量来获得真正的数据成员:理解了这一点,你就知道有虚继承的类中,与虚基类的相关转化是非法的。
同样,指向类的非静态 成员函数的指针也不是指针,不是一个地址,而是一个指向成员函数的指针 。
为了访问该成员,需要将对象的地址(this指针)和成员的偏移量(包含于指向数据成员的指针中)相加。对于指向成员函数的指针的情形,需要将对象的地址用作(或用于计算)this指针的值,进行函数调用,以及作为其他用途。
指向成员函数的指针也表现出一种逆变性,即存在从指向基类成员函数的指针到指向派生类成员函数指针的预定义转化,反之不然。这很好理解,如果考虑到基类成员函数会试图通过其this指针访问基类成员,然而派生类函数可能会试图反问基类中不存在的成员的话。