this指针
一、定义
1.引言
首先,我们都知道类的成员函数可以访问类的数据(限定符只是限定于类外的一些操作,类内的一切对于成员函数来说都是透明的),那么成员函数如何知道哪个对象的数据成员要被操作呢,原因在于每个对象都拥有一个指针:this指针,通过this指针来访问自己的地址。
注意:
this指针并不是对象的一部分,this指针所占的内存大小是不会反应在sizeof操作符上的。this指针的类型取决于使用this指针的成员函数类型以及对象类型。
this指针并不是对象的一部分,this指针所占的内存大小是不会反应在sizeof操作符上的。this指针的类型取决于使用this指针的成员函数类型以及对象类型。
2.this指针的概念
在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。为了深入理解this指针的概念,我们从c++程序到c程序的翻译来深入,c++是在c语言基础上发展而来的,第一个 C++ 的编译器实际上是将 C++ 程序翻译成C语言程序,然后再用C语言编译器进行编译。C语言没有类的概念,只有结构,函数都是全局函数,没有成员函数。翻译时,将 class 翻译成 struct、对象翻译成结构变量是显而易见的,但是对类的成员函数应该如何翻译?对myCar.Modify();这样通过一个对象调用成员函数的语句,又该如何翻译呢?C语言中只有全局函数,因此成员函数只能被翻译成全局函数;myCar.Modify();这样的语句也只能被翻译成普通的调用全局函数的语句。那如何让翻译后的 Modify 全局函数还能作用在 myCar 这个结构变量上呢?答案就是引入“this 指针”。下面来看一段 C++ 程序到C 程序的翻译。
c++程序:
class CCar { public: int price; void SetPrice(int p); }; void CCar::SetPrice(int p) { price= p; } int main() { CCar car; car.SetPrice(20000); return 0; }翻译后的c程序(此程序应保存为扩展名为.c的文件后再编译)
struct CCar { int price; }; void SetPrice(struct CCar* this, int p) { this->price = p; } int main() { struct CCar car; SetPrice(&car, 20000); return 0; }可以看出,类被翻译成结构体,对象被翻译成结构变量,成员函数被翻译成全局函数。但是C程序的全局函数 SetPrice 比 C++ 的成员函数 SelPrice 多了一个参数,就是struct CCar *this。car.SetPrice(20000);被翻译成SetPrice(&car, 20000);,后者在执行时,this 形参指向的正是 car 这个变量,因而达到了 SetPrice 函数作用在 car 变量上的效果。
思考题:以上翻译还不完整,因为构造函数的作用没有体现出来。思考构造函数应该如何翻译。另外,静态成员函数和静态成员变量应如何翻译?
3.this指针的作用
实际上,现在的C编译器从本质上来说也是按上面的方法来处理成员函数和对成员函数的调用的,即非静态成员函数实际上的形参个数比程序员写的多一个。多出来的参数就是所谓的“this指针”。这个“this指针”指向了成员函数作用的对象,在成员函数执行的过程中,正是通过“Ihis指针”才能找到对象所在的地址,因而也就能找到对象的所有非静态成员变量的地址。
下面程序的运行结果能够证明这一点:
下面程序的运行结果能够证明这一点:
#include <iostream> using namespace std; class A { int i; public: void Hello(){ cout << "hello" << endl; } }; int main() { A* p = NULL; p -> Hello(); }程序输出的结果是:hello
在上面的程序中,p明明是一个空指针,为何通过它还能正确调用A的成员函数Hello呢?因为,参考上面的c++到c的程序的翻译,P->Hello()实质上应该是Hello(P),在翻译后的Hello函数中,cout语句没有用到this指针,因此依然可以输出结果,如果Hello函数中有对成员变量额访问,程序就会出错。
c++规定,在非静态成员函数内部可以直接使用this关键字,this就代表指向该函数所作用对象的指针,看下面的例子:
#include <iostream> using namespace std; class Complex { public: double real, imag; Complex(double r, double i) : real(r), imag(i) {} Complex AddOne() { this->real++; return *this; } }; int main() { Complex cl(1, 1), c2(0, 0); c2 = cl.AddOne(); cout << c2.real << "," << c2.imag << endl; //输出 2,1 return 0; }第 9 行this->real++;,this 指针的类型是 Complex*。因为 this 指针就指向函数所作用的对象,所以 this->rear 和 real 是完全等价的。*this代表函数所作用的对象,因此执行第 16 行:c2 = c1.AddOne();,进入 AddOne 函数后,*this实际上就是 c1。因此的 c2 值会变得和 c1 相同。
因为静态成员函数并不作用于某个对象,所以在其内部不能使用 this 指针;否则,这个 this 指针该指向哪个对象呢?
二、注意事项
1.this指针只能成员函数中使用
(1)成员函数默认第一个参数为T* const register this。(友元函数,全局(静态)函数不是成员函数)
(2)创建一个对象后,不能通过对象使用this指针,也无法知道一个对象的this指针的位置,只有成员函数里才有this指针的位置,可以利用&this来获取,也可以直接使用this指针。
2.this指针不能在静态函数中使用
静态函数如同静态变量一样,它不属于具体的哪一个对象,而是表示了整个类范围意义上的信息,但是this指针却是实实在在的对应一个对象,所以this指针不可以被静态函数使用
3.this指针的创建和销毁
this指针在成员函数的开始执行前被构造,在成员的执行结束后被清除
4.this与*this的区别
return *this返回的是当前对象的克隆或者本身(若返回类型为A, 则是克隆, 若返回类型为A&, 则是本身 )。return this返回当前对象的地址(指向当前对象的指针),具体分析如下:
#include <iostream> using namespace std; class A { public: int x; A* get() { return this; } }; int main() { A a; a.x = 4; if(&a == a.get()) { cout << "yes" << endl; } else { cout << "no" << endl; } return 0; } // 结果为:yes #include <iostream> using namespace std; class A { public: int x; A get() { return *this; //返回当前对象的拷贝 } }; int main() { A a; a.x = 4; if(a.x == a.get().x) { cout << a.x << endl; } else { cout << "no" << endl; } if(&a == &a.get()) { cout << "yes" << endl; } else { cout << "no" << endl; } return 0; } //如果返回类型是A&, 那么return *this返回的是当前对象本身(也就是其引用), 而非副本。4
no
三、this指针的操作
1.返回类对象本身或其拷贝
在类的非静态成员函数中返回类对象本身的时候,我们可以使用圆点运算符*,箭头运算符->。示例如下:
#include<iostream> using namespace std; class A{ private: int x; public: A(){ x=0; } void display(){ cout<<this<<endl; cout<<this->x<<endl; cout<<x<<endl; cout<<(*this).x<<endl; } }; int main(void){ A a; a.display(); } //输出: 0x70fe40 0 0 0
2.实现级联
当你进入一个房子后,你可以看见桌子、椅子、地板等,但是房子你是看不到全貌了。对于一个类的实例来说,你可以看到它的成员函数、成员变量,但是实例本身呢?this是一个指针,它时时刻刻指向你这个实例本身。一种情况就是,在类的非静态成员函数中返回类对象本身的时候,我们可以使用圆点运算符(*this).,箭头运算符this->,另外,我们也可以返回关于*this的引用,这样我们可以像输入输出流那样进行“级联”操作。
级联:相当于将多个表中同一数据串起来,变一个都变;
实例如下:我们建立一个学生信息类
#include<iostream> #include<string> using namespace std; class Stu_Info_Mange { int sno; string sname; int age; int grade; public: Stu_Info_Mange(int s=0,string n="",int a=0,int g=0) { sno=s; sname=n; age=a; grade=g; } void Setsname(int sn) //使用this指针进行赋值 { this->sname=sn; } int Setage(int a) { this->age=a; return (*this).age; //使用this指针返回该对象的年龄 } void print() { cout<<"the sname is "<<this->sname<<endl; //显式this指针通过箭头操作符访问 cout<<"the sno is "<<sno<<endl;//隐式使用this指针打印 cout<<"the age is "<<(*this).age<<endl;//显式使用this指针通过圆点操作符 cout<<"the grade is "<<this->grade<<endl<<endl; } }; int main() { Stu_Info_Mange sim1(761,"张三",19,3); sim1.print(); //输出信息 sim1.Setage(12); //使用this指针修改年龄 sim1.print(); //再次输出 return 0; }
以上的例子中,我们要设置一个学生的信息,需要对每一个相关变量所属的成员函数进行调用(名字调用设置名字的成员函数。。。),我们还可以使用this的另外一个功能,让他实现级联,方便我们调用,对此,我们对上述代码进行修改,如下:
#include<iostream> #include<string> using namespace std; class Stu_Info_Mange { int sno; string sname; int age; int grade; public: Stu_Info_Mange(int s=0,string n="",int a=0,int g=0) { sno=s; sname=n; age=a; grade=g; } <span style="color:#ff0000;">Stu_Info_Mange &</span> Setsname(string s) //<span style="color:#ff0000;">所有的相关函数,都返回该对象的引用, //这样就可以实现级联,方便编码</span> { this->sname=s; <span style="color:#ff0000;">return (*this);</span> } <span style="color:#ff0000;">Stu_Info_Mange &</span> Setsno(int sno) { this->sno=sno; <span style="color:#ff0000;">return *this</span>; } <span style="color:#ff0000;">Stu_Info_Mange &Setgrade(</span>int grade) { this->grade=grade; <span style="color:#ff0000;">return *this;</span> } <span style="color:#ff0000;">Stu_Info_Mange &</span>Setage(int age) { this->age=age; <span style="color:#ff0000;">return *this;</span> } void print() { cout<<"the sname is "<<this->sname<<endl; cout<<"the sno is "<<this->sno<<endl; cout<<"the age is "<<this->age<<endl; cout<<"the grade is "<<this->grade<<endl<<endl; } }; int main() { Stu_Info_Mange sim;// 使用默认参数 <span style="color:#ff0000;">sim.Setsname("张三").Setsno(457).Setgrade(2012).Setage(20); //级联</span> //使用this指针使串联的函数调用成为可能 sim.print(); return 0; }