再上一篇:12.2 虚函数
上一篇:12.3 静态成员
主页
下一篇:第十三章 运算符重载
再下一篇:13.2 几个特殊运算符的重载
文章列表

12.5 指向类成员的指针

《VC++程序设计基础》,讲述C++语言的语法和标准库,以及Visual C++ 函数库和MFC类库的使用,并附相关代码示例。

在 C++中可以定义一种特殊的指针,它指向类中的成员函数或类中的数据成员。并可通过这样的指针来使用类中的数据成员或调用类中的成员函数。我们在下面分别讨论之。12.5.1指向类中数据成员的指针变量

定义一个指向类中数据成员的指针变量的一般格式为:

ClassName:: *PointName;

其中type是指针PointName所指向数据的类型,它必须是类ClassName中某一数据成员的类型。我们结合实例来说明这种指针的用法及应注意的事项。

例 12.14 使用类的数据成员的指针来访问成员数据。

#include

class S{

int x;

public:

float y,z;

float a;

public:

S(){x=y=z=a=0;}

S(float b,float c,float d,float e)

{x=b;y=c;z=d;a=e;}

float Getx() {return x;}

void Setyza(float b,float c,float d)

{y=b;z=c;a=d;}

};

void main(void )

{

S s1(100,200,300,400),*ps;

float S::*mptr;

ps=&s1;

mptr = &S::y;

cout <<"ps->y="<y<<'\t';

cout <<"ps->y="<*mptr<<'\t';

cout <<"ps->y="<

mptr = &S::a;

cout <<"ps->a="<a<<'\t';

cout <<"ps->a="<*mptr<<'\t';

cout <<"ps->a="<

}

程序执行后的输出为:

ps->y=200 ps->y=200 ps->y=200

ps-> a=400 ps-> a=400 ps-> a=400

根据上例,对指向类中数据成员的指针变量的使用方法说明以下几点:

1、指向类中数据成员的指针变量不是类中的成员,这种指针变量应在类外定义。

2、与指向类中数据成员的指针变量同类型的任一数据成员,可将其地址赋给这种指针变量,赋值的一般格式为:

PointName = &ClassName::member;

即将类中指定成员的地址赋给指针变量,其中ClassName是已定义的类名,而member是数据成员名。显然,编译系统对类名并不分配存储空间,也就没有一个绝对地址。所以这种赋值,是取该成员相对于该类的所在对象中的偏移量,即相对地址(距离开始位置的字节数)。对类中的任一成员,其偏移量是一个常数。如上例中:

mptr = &S::y;

表示将数据成员y的相对起始地址赋给指针变量mptr。

3、用这种指针访问数据成员时,必须指明是使用那一个对象的数据成员。当与对象结合使用时,其用法为:

ObjectName.* PointName

其中ObjectName为对象名。如例中的:

s1.*mptr

当与指向对象的指针变量结合使用时,其用法为:

Object Point->* PointName

其中Object Point为指向对象的指针变量。如例中的:

ps->*mptr

因这种指针变量的值是一个相对地址,不是指向某一个对象中的数据成员的绝对地址,所以不能单独使用这种指针变量来访问数据成员。如上例中在主函数中增加语句:

cout<<*mptr;

是不允许的。

4、由于这种指针变量并不是类的成员,所以使用它只能访问对象的公有数据成员。若要访问对象的私有数据成员,必须通过成员函数来实现。

12.5.2 指向类中成员函数的指针变量

定义一个指向类中成员函数的指针变量的一般格式为:

(ClassName:: *PointName)();

其中PointName 是指向类中成员函数的指针变量;ClassName 是已定义的类名;type是通过函数指针 PointName调用类中的成员函数时所返回值的数据类型,它必须与类 ClassName中某一成员函数的返回值的类型相一致;是函数的形式参数表。

在使用这种指向成员函数的指针前,应先对其赋值,其方法用指向类中数据成员的指针类同,即:

PointName= ClassName::FuncName;

同样地,只是将指定成员函数的相对地址赋给指向成员函数的指针。

例 12.15 使用成员函数的指针变量。

#include

class S{

int x,y;

public:

S(float a=0,float b=0)

{x=a;y=b;}

float Getx() {return x;}

float Gety() {return y;}

float Setx(float a){ x=a;return x;}

float Sety(float a) {y=a; return y;}

};

void main(void )

{

S s1(100,400),*ps;

float (S::*mptr1)();

float (S::*mptr2)(float);

ps=&s1;

mptr1 = S::Getx;

cout <<"ps->x="<Getx()<<'\t';

cout <<"ps->x="<<(ps->*mptr1)()<<'\t';

cout <<"ps->x="<<(s1.*mptr1)()<

mptr1 = S::Gety;

cout <<"ps->y="<Gety()<<'\t';

cout <<"ps->y="<<(ps->*mptr1)()<<'\t';

cout <<"ps->y="<<(s1.*mptr1)()<

mptr2 = S::Setx;

(ps->*mptr2)(1000);

cout <<"ps->x="<Getx()<

}

执行程序后的输出为:

ps-> x =200 ps-> x =200 ps-> x =200

ps-> y =400 ps-> y =400 ps-> y =400

ps-> x =1000

结合上例,对指向成员函数的指针变量的使用方法说明以下几点:

1、指向类中成员函数的指针变量不是类中的成员,这种指针变量应在类外定义。

2、不能将任一成员函数的地址赋给指向成员函数的指针变量,只有成员函数的参数个数、参数类型、参数的顺序和函数的类型均与这种指针变量相同时,才能将成员函数的指针赋给这种变量。如上例中:

mptr1 = S::Getx;

mptr1 = S::Gety;

都是允许的。但

mptr1 = S:: Setx;

是错误的,虽然有相同的返回值,但函数的参数表是不同的。

3、使用这种指针变量来调用成员函数时,必须指明调用那一个对象的成员函数,这种指针变量是不能单独使用的。与对象结合使用时,其用法为:

(ObjectName.* PointName)(<实参表>);

其中ObjectName为对象名。如上例中的:

(s1.*mptr1)();

与指向对象的指针变量结合作用时,其用法为:

(Object Point->* PointName)(<实参表>)

其中Object Point为指向对象的指针变量。如上例中的:

(ps->*mptr1)();

4、由于这种指针变量不是类的成员,所以用它只能调用公有的成员函数。若要访问类中的私有成员函数,必须通过类中的其它的公有成员函数。

5、当一个成员函数的指针指向一个虚函数,且通过指向对象的基类指针或对象的引用来访问该成员函数指针时,同样地产生运行时的多态性。

6、当用这种指针指向静态的成员函数时,可直接使用类名而不要列举对象名。这是由静态成员函数的特性所确定的。下面的例子说明了指向静态成员函数的指针变量的用法。

例 12.16 用指向成员函数的指针调用静态成员函数。

#include

class S{

int x;

static int y;

public:

S(float a=0,float b=0)

{x=a;y=b;}

static float Gety() {return y;}

static float Getx(S &a) {return a.x;}

};

int S::y=0;

void main(void )

{

S s1(100,400),*ps;

float (*mptr1)();

float (*mptr2)(S &);

ps=&s1;

mptr2 = S::Getx;

cout <<"s1.x="<

cout <<"s1.x="<

mptr1 = S::Gety;

cout <<"s1.y="<

cout <<"s1.y="<

}

执行程序后的输出为:

s1.x=100 s1.x=100

s1.y=400 s1.y=400

说明指向静态成员函数的指针变量的方法与指向一般函数指针类同。如上例中:

float (*mptr1)();

float (*mptr2)(S &);

因静态成员函数不论是否产生对象,其绝对地址已经确定。所以,将静态成员的地址赋给这种指针变量时,须加类名和作用域运算符。如例中:

mptr2 = S::Getx;

由于这种赋值已将函数的绝对地址赋给了指针变量,所以用这种指针调用静态成员函数时,可以直接通过指针变量调用静态成员函数。如例中:

mptr2(s1)

从指针变量的定义和调用方法中可以看出,可用这种指针变量调用具有相同返回值和相同参数的一般函数,也可以用这种指针变量调用任意类中具有相同返回值和相同参数的静态成员函数。

习题与思考12

1.分析使用友元函数的利弊。

2.说明友元函数和友元类的区别和作用?

3.虚函数的主要用途是什么?

4.静态的数据成员与一般的数据成员有什么区别?

5.静态的成员函数与一般的成员函数有什么区别?如何调用静态成员函数?

6.写出以下程序的执行结果:

#include

class A{

int a,b;

static int c;

public:

A(int x) { a=x;}

void f1(float x)

{b=a * x; }

static void setc(int x) { c=x;}

int f2()

{ return a+b+c;}

};

int A::c =100;

void main(void)

{

A a1(1000),a2(2000);

a1.f1(0.25);

a2.f1(0.55);

A::setc(400);

cout<<"a1="<

}

7.建立一条双向无序链表,结点数据包括:姓名,地址和工资。

8.建立一条双向有序链表,结点数据包括:姓名,地址和工资。按工资从小到大的顺序排序。

9.指向类中数据成员的指针变量与一般的指针变量有什么不同?

10.指向类中成员函数的指针变量与一般的指向函数的指针变量有什么不同?