指向类的成员的指针

 

【补足基础】指向类的成员的指针

分类: C++基础   25人阅读  评论(0)  收藏  举报
指向类的成员的指针

    分为指向类的数据成员和成员函数的指针。

    指向数据成员的指针格式如下:
    
<类型说明符><类名>::*<指针名>

    
指向成员函数的指针格式如下:

    <类型说明符>(<类名>::*<指针名>)(<参数表>)

    例如,设有如下一个类A

[cpp]  view plain copy
  1. class A  
  2. {  
  3. public:  
  4.     int fun (int b) { return a * c + b; }  
  5.     A(int i) { a = i; }  
  6.     int c;  
  7. private:  
  8.     int a;  
  9. };  
          定义一个指向类A的数据成员c的指针pc,其格式如下:

      int A:: *pc = &A::c;

    再定义一个指向类A的成员函数fun的指针pfun,其格式如下:
    
int (A:: *pfun)(int) = A::fun;

        由于类不是运行时存在的对象。因此,在使用这类指针时,需要首先指定A类的一个对象,然后,通过对象来引用指针所指向的成员。例如,给pc指针所指向的数据成员c赋值8,可以表示如下:

[cpp]  view plain copy
  1. A a;  
  2. a.*pc = 8;  

        其中,运算符.*是用来对指向类成员的指针来操作该类的对象的。

    如果使用指向对象的指针来对指向类成员的指针进行操作时,使用运算符->*。例如:

[cpp]  view plain copy
  1. A *p = &a;  
  2. p->*pc = 8;<span style="font-family: Arial, Verdana, sans-serif; white-space: normal; font-size: 12.0563px; ">  </span>  

  指向类的数据成员的指针并非指针,因为它既不包含地址,行为也不像指针。

  与常规指针不同,一个指向成员的指针并不指向一个具体的内存位置,它指向的是一个类的特定成员,而不是指向一个特定对象里的特定成员。通常最清晰的做法是将指向数据成员的指针看作为一个偏移量。 C++标准并没有说该如何实现指向成员的指针,大多数编译器都将指向数据成员的指针实现为一个整数,其中包含被指向成员的偏移量。另外加上1(加1是为了让0值可以表示一个空的数据成员指针)。这个偏移量告诉你,一个特定成员的位置距离对象的起点有多少个字节。一个类成员的偏移量在任何对象中都是相同的。

    给定一个成员在类内的偏移量,为了访问位于那个偏移量的数据成员,我们需要该类的一个对象的地址。这时候就需要 .* 和 ->* 这两个看上去非同寻常的操作符闪亮登场了。

[cpp]  view plain copy
  1.  class C   
  2. {    
  3. public:          
  4.    int a_;    
  5. };    
  6.   
  7. int C::*pimC; // 一个指针,指向C的一个int成员    
  8. C aC;    
  9. C* pC = &aC;    
  10. pimC = &C::a_; //::的优先级高于&    
  11. aC.*pimC = 0;    
  12. int b = pC -> *pimC;  

    当写下pC->*pimC时,其实是请求将pC内的地址加上pimC内的偏移量,为的是访问pC所指向的C对象中适当的数据成员。当写aC.*pimC时,是在请求aC的地址加上pimC中的偏离量,也是为了访问pC所指向的C对象中适当的数据成员。

    另外,存在从指向基类成员的指针到指向公有派生类成员的指针的隐式转换,但不存在从指向派生类成员的指针到指向其任何一个基类成员的指针的转换(基类指针可转换为子类指针,反之则不行)。这个可以从c++对象的内存布局来理解,因为基类成员一般在子类对象的前部分,所以这些成员在基类或者子类中的偏移是一样的;解析的时候,就是对象的首地址加上这个偏移量来获得真正的数据成员。理解了这一点,你就知道有虚继承的类中,与虚基类的相关转化是非法的。

    让我们再看看指向一般函数的指针的定义格式:

     <类型说明符>*<指向函数指针名>(<参数表>)

    关于给指向函数的指针赋值的格式如下:
    
<指向函数的指针名>=<函数名>

    关于在程序中,使用指向函数的指针调用函数的格式如下:
    
(*<指向函数的指针名>)(<实参表>)

    
如果是指向类的成员函数的指针还应加上相应的对象名和对象成员运算符。

    下面给出一个使用指向类成员指针的例子:
[cpp]  view plain copy
  1. #include <iostream.h>  
  2. class A  
  3. {  
  4. public:  
  5.     A(int i) { a = i; }  
  6.     int fun(int b) { return a * c + b; }  
  7.     int c;  
  8. private:  
  9.     int a;  
  10.  };  
  11.   
  12. int main()  
  13. {  
  14.     A x(8);        //定义类A的一个对象x  
  15.     int A::*pc;    //定义一个指向类数据成员的指针pc  
  16.     pc = &A::c;    //给指针pc赋值  
  17.     x.*pc = 3;     //用指针方式给类成员c赋值为3  
  18.     int (A::*pfun)(int);  //定义一个指向类成员函数的指针pfun  
  19.     pfun = A::fun;        //给指针pfun赋值  
  20.     A *p = &x;            //定义一个对象指针p,并赋初值为x  
  21.     cout<<(p->*pfun)(5)<<endl;    //用对象指针调用指向类成员函数指针pfun指向的函数  
  22.   
  23.     return 0;  
  24. }  

    以上程序定义了好几个指针,虽然它们都是指针,但是所指向的对象是不同的。p是指向类的对象;pc是指向类的数据成员;pfun是指向类的成员函数。因此它们的值也是不相同的。


对象指针和对象引用作函数的参数

    1. 对象指针作函数的参数

     使用对象指针作为函数参数要比使用对象作函数参数更普遍一些。因为使用对象指针作函数参数有如下两点好处:
    (1)  实现传址调用。可在被调用函数中改变调用函数的参数对象的值,实现函数之间的信息传递。
    (2)  使用对象指针实参仅将对象的地址值传给形参,而不进行副本的拷贝,这样可以提高运行效率,减少时空开销。
[cpp]  view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class M  
  5. {  
  6. public:  
  7.     M() { x = y = 0; }  
  8.     M(int i, int j) { x = i; y = j; }  
  9.     void copy(M *m);  
  10.     void setxy(int i, int j) { x = i; y = j; }  
  11.     void print() { cout<<x<<", "<<y<<endl; }  
  12. private:  
  13.     int x, y;  
  14. };  
  15.   
  16. void M::copy(M *m)  
  17. {  
  18.     x = m->x;  
  19.     y = m->y;  
  20. }  
  21.   
  22. void fun(M m1, M *m2);  
  23. int main()  
  24. {  
  25.     M p(5, 7), q;  
  26.     q.copy(&p);  
  27.     fun(p, &q);  
  28.     p.print();  
  29.     q.print();  
  30.   
  31.     return 0;  
  32. }  
  33.   
  34. void fun(M m1, M *m2)  
  35. {  
  36.     m1.setxy(12, 15);  
  37.     m2->setxy(22, 25);  
  38. }  

    当形参是指向对象指针时,调用函数的对应实参应该是某个对象的地址值,一般使用&后加对象名。下面举一例子说明对象指针作函数参数。

    输出结果为:
    5, 7

    22, 25

    从输出结果可以看出,当在被调用函数fun中,改变了对象的数据成员值[m1.setxy(12, 15)]和指向对象指针的数据成员值[m2->setxy(22, 25)]以后,可以看到只有指向对象指针作参数所指向的对象被改变了,而另一个对象作参数,形参对象值改变了,可实参对象值并没有改变。因此输出上述结果。

    2. 对象引用作函数参数

     在实际中,使用对象引用作函数参数要比使用对象指针作函数更普遍,这是因为使用对象引用作函数参数具有用对象指针作函数参数的优点,而用对象引用作函数参数将更简单,更直接。所以,在C++编程中,人们喜欢用对象引用作函数参数。现举一例子说明对象引用作函数参数的格式。
[cpp]  view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class M  
  5. {  
  6. public:  
  7.     M() { x = y = 0; }  
  8.     M(int i, int j) { x = i; y = j; }  
  9.     void copy(M &m);  
  10.     void setxy(int i, int j) { x = i; y = j; }  
  11.     void print() { cout<<x<<", "<<y<<endl; }  
  12. private:  
  13.     int x, y;  
  14. };  
  15.   
  16. void M::copy(M &m)  
  17. {  
  18.     x = m->x;  
  19.     y = m->y;  
  20. }  
  21.   
  22. void fun(M m1, M &m2);  
  23. int main()  
  24. {  
  25.     M p(5, 7), q;  
  26.     q.copy(p);  
  27.     fun(p, q);  
  28.     p.print();  
  29.     q.print();  
  30.   
  31.     return 0;  
  32. }  
  33.   
  34. void fun(M m1, M &m2)  
  35. {  
  36.     m1.setxy(12, 15);  
  37.     m2->setxy(22, 25);  
  38. }  
    该例子与上面的例子输出相同的结果,只是调用时的参数不一样。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值