指向类的数据成员的指针:
指向数据成员的指针格式如下:
<类型说明符><类名>::*<指针名>
例如,设有如下一个类A:
class A
{
public:
int fun (int b) { return a * c + b; }
A(int i) { a = i; }
int c;
private:
int a;
};
定义一个指向类A的数据成员c的指针pc,其格式如下:
int A:: *pc = &A::c;
再定义一个指向类A的成员函数fun的指针pfun,其格式如下:
int (A:: *pfun)(int) = A::fun;
由于类不是运行时存在的对象。因此,在使用这类指针时,需要首先指定A类的一个对象,然后,通过对象来引用指针所指向的成员。例如,给pc指针所指向的数据成员c赋值8,可以表示如下:
A a;
a.*pc = 8;
其中,运算符
.*
是用来对指向类成员的指针来操作该类的对象的。
如果使用指向对象的指针来对指向类成员的指针进行操作时,使用运算符
->*
。例如:
A *p = &a;
p->*pc = 8;
指向类的数据成员的指针并非指针,因为它既不包含地址,行为也不像指针。
与常规指针不同,一个指向成员的指针并不指向一个具体的内存位置,它指向的是一个类的特定成员,而不是指向一个特定对象里的特定成员。
通常最清晰的做法是将指向数据成员的指针看作为一个偏移量
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
对象中适当的数据成员。
指向类的成员函数的指针:
指向成员函数的指针格式如下:
<类型说明符>(<类名>::*<指针名>)(<参数表>)
下面给出一个使用指向类成员指针的例子:
#include <iostream.h>
class A
{
public:
A(int i) { a = i; }
int fun(int b) { return a * c + b; }
int c;
private:
int a;
};
int main()
{
A x(8); //定义类A的一个对象x
int A::*pc; //定义一个指向类数据成员的指针pc
pc = &A::c; //给指针pc赋值
x.*pc = 3; //用指针方式给类成员c赋值为3
int (A::*pfun)(int); //定义一个指向类成员函数的指针pfun
pfun = &A::fun; //给指针pfun赋值 需要&符!!!
A *p = &x; //定义一个对象指针p,并赋初值为x
cout<<(p->*pfun)(5)<<endl; //用对象指针调用指向类成员函数指针pfun指向的函数
return 0;
}
总结:
类成员指针指向的不是实际数据,使用类成员指针需要绑定到具体对象。
初始化一个成员指针或者为一个成员指针赋值时,该指针没有指向任何数据。成员指针指定了成员而非该成员所属的对象,只有当解引用成员指针时我们才提供对象。
参考文献:
- C++ primer 第5版 ch19.4
-
指向类的成员的指针