【c++笔记八】成员指针和this指针

来源:互联网 发布:淘宝买保险好吗 编辑:程序博客网 时间:2024/06/03 09:13
2015年1月29日 冰雨周四
长沙今天简直冷炸了!!!眼看要下雪的节奏啊,冷死爹了!!!昨晚还被CSDN坑死了,还得我把【c++笔记七】后三分之一又写了一遍。
今天心累啊!就少写一点知识点好了(预告:周日更新运算符重载!)。
——————————————————————分割线—————————————————————————

一.成员指针

这里说到的成员指针,可不是指在类中声明的指针类型的成员变量。而是在类外声明的,指向类中某一个成员变量或者成员函数的指针。但实际上声明后该指针属于该类的成员变量。
这里还要注意权限控制的问题。因为是在另外调用类中的成员,所以这个成员只有是public权限的才能被类外的指针调用。

1.指向成员变量的指针

怎样定义一个能够指向类中某个成员变量的指针呢?比如现在我们这有这样的一个类:
class A{public:    int a;};
我们首先要确定这个指针的数据类型要和成员变量的类型一样(这个类中是int)。然后我们是指向类的成员变量,并不依赖任何一个类实例化的对象,所以这个指针应该直接由类名作用域确定。
指向成员变量的指针的应该这样定义:成员变量类型类名::*指针名 = &类名::成员变量名;(如:int A::*p = &A::a ;)
如果要调用这个指针的话, 需要用实例化的对象来调用,像调用public类型的成员变量一样:对象名 . *成员指针名
看看代码吧:
#include <iostream>using namespace std;class A{public:    int x;    A(int x=0):x(x){}};int main(){    int A::*pmem = &A::x;    A a;    cout<<a.*pmem<<endl;    A* pa = new A(10);    cout<<pa->*pmem<<endl;    return 0;}

太简单了,没什么好说的。继续吧。
我们来说一下成员变量指针的本质吧。成员变量指针的本质是:记录指针在对象首地址中的偏移量。
意识就是说,这些变量指针的值是一个个的偏移量。这个偏移量是:该成员指针所指向的成员变量相对于该对象首地址的偏移量。
光说没用,我们来验证一下看看是不是吧。
#include <iostream>using namespace std;class A{public:    int x;    int y;    int z;    A(int x=0,int y=1,int z=2)        :x(x),y(y),z(z){}};int main(){    int A::*px = &A::x;    int A::*py = &A::y;    int A::*pz = &A::z;    A a;    cout<<a.*px<<" "<<a.*py<<" "<<a.*pz<<endl;    cout<<px<<" "<<py<<" "<<pz<<endl;    return 0;}

哎!怎么全是1啊!!!因为编译器在搞鬼,为了保证类的封装性,它隐藏成员变量指针的真实值(偏移量)。
不行啊!我一定要看到偏移量!有什么办法吗?当然有啊,我们可是程序猿啊!!!我们放大招,祭出——union!一直没怎么用过它吧,没事,我带你一起来用一次!
#include <iostream>using namespace std;class A{public:    int x;    int y;    int z;    A(int x=0,int y=1,int z=2)        :x(x),y(y),z(z){}};int main(){    union{        int A::*px;        int* pa;    };    px = &A::x;    union{        int A::*py;        int* pb;    };    py = &A::y;    union{        int A::*pz;        int* pc;    };    pz = &A::z;    A a;    cout<<a.*px<<" "<<a.*py<<" "<<a.*pz<<endl;    cout<<pa<<" "<<pb<<" "<<pc<<endl;    return 0;}
  union应该都清楚吧?所有成员共享同一块内存。就算你把我的成员指针的值隐藏了,哼,小样!我还有同伙知道你的值!
看看,是不是我说的那样,一个int 4个字节,所以第一偏移量为0,第二个是4,第三个是8。

2.成员函数的指针

其实成员函数指针的用法和成员变量指针相似,并且更像函数指针的用法。
定义:成员函数的类型(类名::*指针名) (参数列表) (如:int(A::*p) () )
赋值:指针名 = &类名::函数名;(如:p =&A::show;)一定不要忘记那个”&“符号
使用:(对象名.*指针名)(参数列表)(如:(a.*p) () )
写个程序你就能懂了!
#include <iostream>using namespace std;class A{public:    int x;    A(int x=0):x(x){}    int add(int y){        cout<<"add(int)"<<endl;        return x+y;    }};int main(){    int (A::*p)(int)=&A::add;    A a(10);    cout<<(a.*p)(2)<<endl;    return 0;}

完全可以参考函数指针的用法哦。


二.this指针

1.this指针就是指向当前对象的指针

其实,在类的任何成员函数中都有this指针的身影。你说我怎么没有看见呢?因为聪明的编译器会在程序编译时为你自动加上this指针。
就拿类A中一个最简单的成员函数来说:
void show(){    cout<<x<<endl;}
其实这个成员函数在编译器编译之后是这样的:
void show(A* this){    cout<<(this->x)<<endl;}
参数列表中的A* this指针是编译器自动为你加上的。成员变量x实际是this->x。
而这个this指针,恰好就是A*类型的,所以:this指针就是指向当前对象的指针
我们来用程序证明一下(需要用到一个知识点:typeid,如果不会的百度查一下,很简单很好用的东西。是用来查一个变量的类型的)
#include <iostream>#include <typeinfo>using namespace std;class Test{public:    void show(){        cout<<"this指针的类型:"<<typeid(this).name()<<endl;    }};int main(){    Test a;    a.show();    return 0;}

看见没有,this指针的类型就是P4Test。p代表这是个指针,4代表这个变量名有四个字节,Test表示这个指针指向Test这个类。不就印证了那句话吗:this指针就是指向当前对象的指针

2.this指针的应用

this指针有什么用呢?有三点:

(1)区分成员变量和参数

还记得我们在说构造函数的初始化列表的时候,说过它的一个功能吗?那就是区分成员变量和参数。
果不在构造中又不能使用初始化列表我们怎么区分成员变量和参数呢?
我们来看一个程序:
#include <iostream>using namespace std;class A{    int x;public:    A(int x=0):x(x){}    void setA(int x){        x = x;    }    void show(){        cout<<x<<endl;    }};int main(){    A a(10);    a.show();    a.setA(20);    a.show();    return 0;}


我们发现通过调用成员setA函数并没有改变成员变量的值。为什么呢?因为在第9行,编译器以为你是在对setA的形参x操作,而不是成员函数(因为名字一样,编译器也犯傻了)。
如果我们用上this指针,就能很好的解决这个问题!
#include <iostream>using namespace std;class A{    int x;public:    A(int x=0):x(x){}    void setA(int x){        this->x = x;    }    void show(){        cout<<x<<endl;    }};int main(){    A a(10);    a.show();    a.setA(20);    a.show();    return 0;}

通过this指针,我们很好的区分了成员变量和函数的形参同名时带来的重名问题。

(2)this指针作为函数的参数值

如果某个某个函数接受的参数列表中有一个该类类型的指针,我们就可以传递this指针。
看代码:
#include <iostream>using namespace std;class A;void showX(A* a);class A{public:    int x;    A(int x=0):x(x){}    void show(){        cout<<"A::show()"<<endl;        showX(this);    }};void showX(A* a){    cout<<"showX(A*)"<<endl;    cout<<a->x<<endl;}int main(){    A a(10);    a.show();;    return 0;}
注意4、5行为类的声明和函数的声明。如果不加这两句会发生什么就当做作业交给你去试验了(如果不懂,说明声明和定义的区别你还不是太懂)
在第16行,我调用了showX函数,而函数showX接受的参数是A类的指针。我们在成员函数的show中把this可以作为A*传给showX。所以才有如上的结果。

(3)this作为函数的返回值

this都可以作为参数传出去,作为返回值返回出去也很正常啊!
还是用程序好说明:
#include <iostream>using namespace std;class A;void showX(A* a);class A{    int x;public:    A(int x=0):x(x){}    void show(){        cout<<x<<endl;    }    A add1(){        ++x;        return *this;    }    A& add2(){        ++x;        return *this;    }};int main(){    A a(10);    a.add1().add1().add1();    a.show();    a.add2().add2().add2();    a.show();    return 0;}
我先来考考你,你知道会输出什么结果吗?



还不知道为什么的,自弹小JJ十下。好好再把引用看一遍。
*this当然就是A类型啦,所以返回*this当然没有问题啊!


——————————————————————————结束语————————————————————————
今天的知识点真不是很多,如果你还有什么不懂的,抓紧回头复习一下前面提到的知识啦。
总结一下:本文讲了成员变量的指针(其值就是偏移量)和成员函数的指针,以及如果在类外使用他们。还分析了this指针是什么,以及怎么去使用this指针。






0 0
原创粉丝点击