1 指向类成员的指针
1.1 概念
与常规指针不同,一个指向类成员的指针并不指向一个具体的位置,它指向的是一个类的特定成员,而不是一个特定对象里的特定成员。通常将指向数据成员的指针看作为一个偏移量。大多数编译器都将指向数据成员的指针实现为一个整数,其中包含被指向的成员的偏移量,另外加上1(加1是为了让值0可以表示一个空的数据成员指针)。这个偏移量告诉你一个特定成员的位置距离对象的起点有多少字节。声明一个类成员指针的语法如下:
TYPE classname::* var;
eg:
class C
{
public:
int a_;
};
int c::*pimC;//一个指针,指向C的一个int成员
C ac;
C *pC = &aC;
picmC = &C::a_;
aC.*pimC = 0;
int b = pC->*pimC;
将pimC的值设置为&C::a_时,实际上是将pimC设置为a_在C内的偏移量。除非a_是静态成员,否则表达式&C::a_中&并不会带来一个实际的地址,而是一个偏移量。这个偏移量适用于类型C的任何对象,即类型C产生的任何对象都可以使用pimC。当写下pC->*pimC时,其实就是将pC内的地址加上pimC内的偏移量。当写下aC.*pimC时就是将a内的地址加上pimC内的偏移量。
1.2 特性
指向类成员的指针存在从指向基类成员的指针到指向共有派生类成员的指针的隐式转换,但不存在从指向派生类成员的指针到指向其任何一个基类成员的指针的转换。
在C++中存在从指向派生类的指针到指向其任何共有基类的预定义转换,而指向类成员的指针正好与此相反。例如:Circle类继承致Shape类。一个Circle*类型的指针可以转换为一个Shape*类型的指针。
class Shape
{
Point center;
};
class Circle : public Shape
{
double radius;
}
一个指向Circle类成员的指针并不能转换为指向Shape类成员的指针,因为指向类成员的指针为偏移量,而父类如Shape中可能不存在子类中的这个成员如Circle中的radius。相反,指向父类成员的指针可以转换为指向子类成员的指针,因为子类中继承了父类中的成员,所以父类中的任何偏移量在子类中一样有效。
2 指向成员函数的指针
获取非静态成员函数的地址时,得到的不是一个地址,而是一个指向成员函数的指针。
class Shape
{
public:
void moveTO(Point location);
bool validate() const;
};
class Circle: public Shape
{
bool draw() const;
};
void (shape::*mf1) (Point) = &Shape::moveTO;
bool (shape::*mf2) () = &Shape::validate;
与指向普通函数的指针不同之处是变量名前多了classname::。和指向数据成员的指针一样,为了对一个指向成员函数的指针进行解引用,需要一个对象或一个指向对象的指针。
Circle circ;
Shape *pShape = ˆ
(pShape->*mf2)();//调用Shape::validate
(cir.*mf2)();//调用Shape::validate
->*和.*必须加上(),因为它们比()操作符优先级低。
指向成员函数的指针与指向数据成员的指针一样存在逆变性。例如:
class B
{
public:
void bset(int val) {bval = val;}
private:
int bval;
};
class D : public B
{
public:
void dset(int val){dval = val;}
private:
int dval;
};
B b;
D d;
void (B::*f1) (int) = &D::dset;//错误
(b.*f1)(12);//错误,B中不存在dval
void (D::*f2)(int) = &B::bset;//ok
(d.*f2)(11);//ok