嗯,同学们好,接下来我们要讲一讲这个This 指针。 This 指针。嗯, This 指针 它是起什么作用的呢?我们要先要从这个C++程序到C程序的翻译来说起。 就是这个C++原刚刚出来的时候 ,并没有这个 ,嗯, 编译器。那怎么编译C++的程序呢? 嗯,就可以通过把,嗯,一段C++程序翻译成C程序。然後再用C的编译去编译。 用这个办法来。 那,那我们看看像这样一段C++的程序,我们如果想把它翻译成 C程序的话,大家想想会是个什么样子?啊? 首先我们看,Class在C语言里面是没有的,对吧?那Class 对应什么呢? Class 当然就对应于C语言里面的Struct 结构体,对吧?那这个 Car里面的这个成员变量, 它 对应于结构体里面的什么呢?当然也对应于结构体里面的那些int对吧?所以说着个Class 我们嗯,大概知道怎么翻译。怎么翻呢?嗯,我们把这个Class CCar翻译成了struct CCar。 然,然後这个成员变量的Price, 变成这个Struct里面的一个int的price. 那问题是,这个成员函数到底应该怎么翻译? 对吧?因为在C语言里面没有成员函数这个概念。好。 这个是成员函数我们函数提起来,这,在C语言里面呢,当然我们知道 只有全局函数无所谓什么成员函数,对吧?那么我们也只能把这个成员函数 翻译成一个全局的函数了。 但是我们在翻译成全局函数的时候我们看到,在这里多加了一个 参数,啊,这个参数它的类型是一个指针,它是指向C。 Struct CCar这种结构体,变量的这个,这个指针,第二个差数不变。 然后在这里面这条语句,price = p,我们把它变成 this->price =p。那这为什么要加一个this这样的指针? 我们再往下看就会知道。好,那接下来我们要翻译的就是,就是这个,这个main了。 main函数当然前面这个基本上可以照抄,但是不好翻译的是这条语句,对吧? SetPrice。 现在我们看看这个C++程序,是调用了SetPrice这个成员函数。 然后这个SetPrice它是作用在car这个对象上面的。 也就是说我们走到这个SetPrice函数里面以后, 在这里修改了price,修改了price这个改的是谁的price呢? 当然是这个car这个对象的price. 对吧?那现在我们把main 翻译,翻译成这个C程序的话它应该是这样。嗯,这个定义的一个CCar的对象,这里对应于,对应于一个 CCar的结构变量。然後这个SetPrice,调用SetPrice,那么我们当然这里也应该要调用SetPrice函数,对吧? 但是,这个SetPrice呢在C++程序里面呢是作用在car这个对像上面的。 它要修改car的成员变量。 那在C原呢?翻译过来C原程序里面,我们怎么做到这一点呢? 那当然具体的办法就是我们调用SetPrice,然後我以这个car的,这个,嗯, 地址作为参数。去调用SetPrice,嗯,car的地址作为第一个参数 调用SetPrice, 当然这个要修改的,的这个价格就作为第二个参数。 接下来我们就看到进到这个,进到这个,呃,嗯,SetPrice这个全局函数里面以后,诶,这个this指针 指向谁啊?是不是就指向这个car对象? 那么我们在这里执行了this -> price = p,大家说 会修改了哪一个变量的这个,修改了谁的price的值呢? 当然就是修改了这个这个car的,这个price的值,对吧? 所以我们看到这个从C++程序翻译到C程序。 其实最难的一点,最不容易理解的一点嘛,就是一个成员函数是怎么翻译的。那我们发现,诶,成员函数 会被翻译成一个全局的这个函数。然後呢这个全局的函数它的差数个数 要比成员函数多一个。多出来的这个,多出来这个差数个数是什么呢? 就是所谓的一个this指针。那这个this指针指向谁呢? 就指向在C++程序里面那个成员函数所作用的这个对像。 来看是不是这样。 嗯,那实际上,嗯,实际上你完全可以这样理解。 就是C++的编译,你就完全可以把它理解成先翻译成C,然後再拿C去 C的编译去编译。嗯,你可以完全那么去理解,这是没有问题的。 那实际上我们也就是说你看到C++程序里面一个成员函数里面他有多少个参数, 那,那真实的这个被,编,编译成机器指令以后的这样一个程序 里面成员函数所对应的那个机械指令面,机器指令里面的那个函数 它的参数实际上要增加一个,增加的那个参数就是所谓的this指针。 那这个this指针呢我们是 可以在,在C++里面这个this指针我们可以把它用在这个 成员函数里面,就是我们在写成员函数的时候,在里面可以写this指针。 那this指针它是做什么用的呢?它的作用就是指向成员函数所作用的那个对像。 我们说一般来说就是非静态的成员函数嘛,它都会具体作用于 某一个,这个对像。对吧?那我们如果在这个成员函数里面写了这个this指针, 这个this指针就会指向这个函数所做用的对象。 我们具体看看一个例子。这里有一个附属类Complex。 然後这里面,嗯,它有这个print的输出实部和虚部的值。 然後这个是构造函数。嗯,构造函数的后面跟了一个初始化列表 就能够把实部初始化成r,虚部初始化成这个i。嗯,然後这儿有个AddOne成员函数。 AddOne成员函数,嗯,它的作用就是把 实部加一然後并且把这个,把实部和虚部都打出来。 在这里我们看到,嗯,在AddOne里面我们使用了这个this指针。我们使用this->real++, 然後this->print。那按照我们前面说的,这个this指针 它就指向了 这个AddOne这个成员函数所作用的那个对象。 那实际上这个this->real++它就等价于real++,啊。 你写real++或者this->real++ 是没有差别的。这个this->print呢它也等价于print 的。然後,然後这个AddOne呢它 返回只是一个Complex对像。我们在这个程序里面希望它能够返回,这个AddOne 所作用的对像自身。就AddOne作用在一个对像上面,然後它把这个对像的实部加一,并且输出它的实部和虚部。 之後呢,这个AddOne能返回这个对像自身。啊。那我们看看我们怎么使用这个AddOne。 如果我们在这个,这个main里面,我们定义了,嗯, Complex c1、c2。c1的实部是,嗯,是,是1 ,虚部是1。 呃,c2的实部和虚部都是0。然後c2 = c1.AddOne. 那我们先看这个c1.AddOne。c1.AddOne的话,就会调用这个,AddOne的这个成员函数。 那走到这成员函数里面来,this->real++这个this指向谁啊? 啊,前面说了,this指针就会指向 这个,成员函数所作用的那个对像。那现在这个AddOne成员函数是作用于c1的,所以 这个this指向的就是 什么啊?就是这个c1.那this->real当然就是c1的real了,对吧? 所以说,嗯, 这条语句执行完了以后呢,嗯, 这个c1它的实部就加了一,而且执行的过程中会把这个c1它的实部和虚部都给它输出出来。 然后我们看这个AddOne它要返回一个,返回一个对像,对吧?我们 在这段程序里面我们,我们希望AddOne返回就是c1这个对象自身。而且是被修改以后的这个c1。 那你在这个AddOne成员函数里面怎么做到这一点呢?你如何去返回 AddOne 所作用的这个对像这个东西? 嗯,这个时候我们就 必须要使用到这个,这个this 指针了。嗯,因为this指针就指向了AddOne所作用的对像。 那么*this就等价于是AddOne所作用的那个对象,对吧?所以我们在这里return*this。 相当于在这个位置AddOne就返回了哪个对像啊?返回了就是c1 这个,这个对像。然后把c1这个对象呢则负责了给c2。 那所以这个程序它,嗯, 它输出的结果就是2,1。嗯, 因为在AddOne执行的过程中,你修改了实部和虚部的值,又把实部和虚部都打出来了。 那我们还可以通过,这个看上去稍微 有一点怪的程序进一步说明这个this指针它的 这个作用。嗯,我们看看这个程序的这个class A 有一个 成员变量i,然後它有一个这个这个 成员函数。嗯,这个成员函数输出"hello"。 然後在main里面呢,我们定义了一个,嗯, class A的指针,让它成为一个空指针。 然後我们通过这个空指针去调用Hello。 这条语句怎么解释呢?这条语句按理说应该说的是 嗯,我们要调用Hello,然后要Hello作用在 p所指向的那个对像上面,对吧?就是我们前面所学的这个知识。当然,大家一看就觉得这个东西 很,很怪异,对吧?因为p它是一个空指针。它没有指向任何的对像。那么你现在 调用Hello,想要Hello作用在p所指向的对像上面。然后, p没指向任何对像。 这个,这个看上去肯定是不对的,对吧?嗯,就是你这么直观一看就觉得,诶?这个程序肯定是出错了。 因为我们前面就强调过很多遍,就是说这个,这个空指针,嗯, 是不能拿来乱用的,对吧?现在这个p是一个空指针,你还要通过这个空指针去调用Hello。 这肯定结果不对嘛!但实际上你要运行一下这个程序, 你会发现这个程序它是不会出错的,它会输出Hello。 然后这看上去就很怪哦,这个-这个p它没有指向任何对象,然后你这Hello还作用在 也就不能作用在任何对象上面,那怎么还能够正确的执行并且有数字结果呢? 看上去很怪,但是实际上呢这个是没有问题的,我们可以 如果你理解力了this指针的含义,你就会知道这个为什么会是正确的。 来想想看,这个Hello这个函数 它是一个成员函数,那我们把它 真正编印器把它编印成一个机器指令的函数了以后呢,那我们前面说的 参数的个数会多出来一个,你可以想象先把iii程序 翻译成c程序然后再去编一个对吧,那翻译成c程-c程序以后 这个它所对应的这个c的那个程序函数它会参数多出一个来,就是一个this指针。然后这边 这里面该-该怎么翻译呢?这里面照样是一样的是cout<<hello<<endl的对吗? hello<<end-l 是这样的。那-那我们看看这个p箭号Hello 被翻译成什么?被翻译成Hello(p)对吗? 我们回顾一下前面说的那个 this指针的这个-这个作用。 这个程序函数Hello所对应的那个c程序里面的全局函数,它是要 有一个this指针作为参数的对吧!那这个this指针又指向了什么啊?指向 Hello所作用的那个对象,那现在,在这条语句里面,Hello所作用的对象应该是由p这个指向所指向的 因此我们把p作为参数写在Hello这个,这个是很顺理成章的对吧! 那现在我们看看实际上就调用了这个被翻译以后的这个c程序里面的Hello 就是这一条语句被翻译成c程序就是这个样子的,那实际上调用Hello (p)实际上就会执行这-这个函数对吧! 在执行这个函数的过程中我们看看做了什么,就执行了cout<<hello 那-那cout<<hello当然它不需要任何什么class A的对象存在,这条语句就能够正确执行 没有问题的是吧,所以这个程序它就输出了这个Hello,它实际上不会出错。 那-那好像跟我们前面学的这个常识有点违反,但实际上我们如果理解力这个this指针的含义 就知道这个是没有问题的。但是我再看下下面的一个例子,会出错的这个例子。 看这,如果我们把Hello成员函数改写了 改成Cout<<i, 然后再Hello<<end 那么这个程序就会出错了。下面这一块还是一样的 还是调用这个-这个-这个p箭号Hello,main是不变的。 那如果我们He-Hello的成员函数这么写,这个程序就会出错了。 为什么呢?我们还是要把这个整个的成员函数翻译成c 程序的全局函数,翻译完了那我们再来看看,翻译成这个c的全局函数之后就变成这样 然后这里面呢我们就cout<<this箭号i对吧! this,这个i是谁的i啊?就是this指针所指向的那个对象它的i,所以这里就变成了this箭号i。 然后这下面的p箭号Hello,还是Hello(p)对吧!那我们要看程序执行到这的时候是不是会有问题。 为什么有问题啊?因为我们看这里执行了Hello(p),p 是时差,那进到这个函数里面以后呢p就跟这个形参this是相等的对吧? 那p是NULL,那进到这个Hello函数里面以后,this也就是NULL,是个空指针。 那现在这个this箭号i就有问题了,因为this是个空指针,然后你要去找这个空指针所指向的一个对象里面的i 那当然是找不到的对吧!所以这条语句事实上就会产生这个 常见的那一种空指针的这个错误,它可能会导致你的程序崩溃。 这样一个程序就是不正确的了。那通过这两个例子我们应该能够很好的理解this指针到底是做什么用的。 this为NULL,可能这条语句就会出错了。那最后再讲一点。 就是因为这个静态成员函数啊它并不会具体作用于某一个对象 而这个this指针呢又是指向成员函数所作用的那个对象 所以说你在静态成员函数之中肯定就不能使用this指针。 你如果使用的话,这个this指针指向谁呢?这个静态成员函数并没有作为任何对象 那所以说啊,这个静态成员函数中它的真实的参数的个数 就跟你在程序里面写的那个参数的个数是一样的。 而普通的成员函数它的真实的参数个数是比你写出来的 要多一个的,多出来的这个实际上就是this指针。