this指针详解

来源:互联网 发布:数控机床编程入门斯沃 编辑:程序博客网 时间:2024/06/06 03:30

什么是this指针?

c++类的成员函数和c语言的函数一样,编译器会改变函数的名称,并为它增减参数(this指针),所以类中并没有储存成员函数信息。但当对象调用函数时,编译器可以确定这些函数的地址。

例如下面代码:

#include<iostream>using namespace std;class Date{public:    Date(int year,int month,int day)        :_year(year),        _month(month),        _day(day)    {}    void print()    {        cout<<_year<<endl;        cout<<_month<<endl;        cout<<_day<<endl;    }private:    int _year;    int _month;    int _day;};int main(){    Date d1(2017,10,24);    Date d2(1900,1,1);    d1.print();    d2.print();    cout<<sizeof(d1)<<endl;//12字节    cout<<sizeof(d2)<<endl; //12字节      return 0;}

上面代码创建了一个日期类,然后分别创建了两个两个日期对象d1和d2,通过sizeof()测得两个对象的大小都为12,这验证了我们上面说法~成员函数信息并不在类中储存。也就是说,创建对象时,成员函数并没有在栈上开辟空间,事实上,所有对象的成员函数只在公共代码段保存了一份。
这里写图片描述

了解到上面这些,肯定会有疑问,d1和d2两个对象用着相同的成员函数,但是d1.print()和d2.print()输出的却是各自的值,那么为什么这些成员函数都访问的各自对象的数据成员呢?

先看一下编译器(vs2008)是怎样处理的,截取一段反汇编代码:

    d1.print();01311410  lea         ecx,[d1] 01311413  call        Date::print (131103Ch)     d2.print();01311418  lea         ecx,[d2] 0131141B  call        Date::print (131103Ch) 

发现编译器在调用函数时,给ecx寄存器分别存入了d1和d2的地址,其实d1和d2的地址就是this指针,即指向当前对象的指针,传入this指针的目的就是为了操作对象里的数据。

事实上,对于上面Date类中的print()函数,会被处理成

void print(Date* const this){    cout<<this->_year<<endl;    cout<<this->_month<<endl;    cout<<this->_day<<endl;}

成员函数参数会增加一个类类型的this指针,对象的地址会被赋给这个指针,然后指针访问对象的数据成员,但是这些都是编译器自动实现的(__thiscall调用约定),我们不能在形参中添加this指针。

__thiscall调用约定
①__thiscall只能够用在类的成员函数上。
②参数从右向左压栈。
③如果参数个数确定,this指针通过ecx传递给被调用者;如果参数不确(_cdecl),this指针在所有参数被压栈后压入堆栈。
④对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈

this指针的使用

对于this指针的使用,一种情况是,在类的非静态成员函数中可以返回对象本身,直接return *this;另一种情况是,当参数与成员变量名相同时,如this->year = year,不能写成year =year。

整理几个关于this指针的问题

  • this指针是什么时候创建的?
    ✍:this在成员函数的开始执行前构造的,在成员的执行结束后清除。

  • 为什么叫this指针,而不是引用呢?
    ✍:首先应该明确的是指针和引用在底层的实现是相同的,之所以叫this指针,是因为最开始将C++称作带类的C,而引用则是在C++1.0版才加入使用的,因此叫做this指针。

  • this指针存放于何处?堆、栈、全局变量还是其他地方?
    ✍:this指针会因编译器不同,而放置的位置不同。可能是栈,也可能是寄存器,甚至全局变量。

  • this指针是如何访问类中的变量的?
    ✍:如果不是类,而是结构的话,那么,如何通过结构指针来访问结构中的变量呢?如果你明白这一点的话,那就很好理解这个问题了。在C++中,类和结构是只有一个区别的:类的成员默认是private,而结构是public。this是类的指针,如果换成结构,那this就是结构的指针了。

  • 为什么this指针不能再静态函数中使用?
    ✍: 静态成员函数并不是针对某个类的实例对象,而是属于整个类的,为所有的对象实例所共有。它在作用域的范围内是全局的,独立于类的对象之外的。它只对类内部的静态成员变量做操作。当实例化一个类的对象时候,里面不存在静态成员的。this指针是相当于一个类的实例的指针,this是用来操作对象实例的内容的,既然静态成员函数和变量都是独立于类的实例对象之外的,它就不能用this指针,也不能操作非静态成员。

  • 我们只有获得一个对象后,才能通过对象使用this指针,如果我们知道一个对象this指针的位置,能否直接使用?
    ✍:this指针只有在成员函数中才有定义。因此,你获得一个对象后,也不能通过对象使用this指针。所以,我们也无法知道一个对象的this指针的位置(只有在成员函数里才有this指针的位置)。当然,在成员函数里,你是可以知道this指针的位置的(可以&this获得),也可以直接使用的。

  • 每个类编译后,是否创建一个类中函数表来保存函数指针,以便来调用函数?
    ✍:普通的类函数(不论是成员函数,还是静态函数),都不会创建一个函数表来保存函数指针的。只有虚函数才会被放到函数表中。但是,既使是虚函数,如果编译器能明确知道调用的是哪个函数,编译器就不会通过函数表中的指针来间接调用,而是会直接调用该函数。

  • this指针如何传递给类中函数的?绑定?还是在函数参数的首参数就是this指针?
    ✍: 大多数编译器通过ecx寄存器传递this指针。事实上,这也是一个潜规则。一般来说,不同编译器都会遵从一致的传参规则,否则不同编译器产生的obj就无法匹配了。在call之前,编译器会把对应的对象地址放到eax中。this是通过函数参数的首参来传递的。this指针在调用之前生成,至于“类实例后函数”,没有这个说法。类在实例化时,只分配类中的变量空间,并没有为函数分配空间。自从类的函数定义完成后,它就在那儿,不会跑的。

  • this指针有没有可能为NULL呢?
    有可能,例如下面代码

class CTest{public:    void FunTest()    {}};void FunTest(){    CTest* pt = NULL;    pt->FunTest();}
原创粉丝点击