1、声明一个指向成员的指针:
int *ip; //一个指向int的指针
int C::*pimC; //一个指针,指向C的一个int成员
所要做的全部事情是使用classname::* 而不是普通的*,来表明你在指向classname的一个成员。
在其他方面,语法和常规的指针声明符一样。
2、一个常规的指针包含一个地址。如果解引用该指针,就会得到位于该地址的对象:
int a = 12;
ip = &a;
*ip = 0;
a = *ip;
与常规指针不同的是,一个指向成员的指针并不指向一个具体的内存位置。
它指向的是一个类的特定成员,而不是指向一个特定对象里的特定成员。
通常最清晰的做法是将指向数据成员的指针看作为一个偏移量。
然而大多数编译器都将指向数据成员的指针实现为一个整数,其中包含被指向的成员的偏移量,另外加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;
将pimC的值设置为&C::a_时,实际上是将pimC设置为a_在C内的偏移量。
除非a_是静态成员,否则在表达式&C::a_中使用&并不会带来一个地址,而是一个偏移量。
注意, 这个偏移量适用于类型C的任何对象,换句话说,如果在一个C对象内成员a_的距离起点的偏移为12字节,那么在任何其他C对象中,a_距离起点的偏移都是12字节。
3、给定一个成员在类内的偏移量,为了访问位于那个偏移量的数据成员,我们需要该类的一个对象的地址。
这时候就需要.*和->*这两个操作符了。
当写下pC->*pimC时,其实是请求将pC内的地址加上pimC内的偏移量,为的是访问pC所指向的C对象中适当的数据成员。
当写aC.*pimC时,是在请求aC的地址加上pimC中的偏移量,也是为了访问pC所指向的C对象中适当的数据成员。
4、在C++中,存在从指向派生类的指针到指向其任何公有基类的预定义转换。
在指向类成员的指针的情况下,存在从指向基类成员的指针到指向公有派生类成员的指针的隐式转换,但不存在从指向派生类成员的指针到指向其任何一个基类成员的指针的转换。
(指向数据成员的指针并非指向一个对象的指针,而只是对象内的一个偏移)
class Shape {
//......
Point center_;
//......
};
class Circle : public Shape {
//....
double radius;
//.....
};
因为一个Circle也是Shape,所以一个Circle对象内包含一个Shape子对象。
因而,Shape内的任何偏移量在Circle内也是一个有效的偏移量。
Point Circle:: *loc = &Shape::center_; //ok,从基类到派生类的转换
然而,一个Shape未必是一个Circle,因此一个Circle的成员的偏移量在Shape内未必是一个有效的偏移量。
double Shape::*entent = &Circle::radius_; //错误,从派生类到基类的转换