【C++】this指针解析

来源:互联网 发布:美国大学放假知乎 编辑:程序博客网 时间:2024/05/17 21:52

想了解this指针,我们先来思考四个问题:

  • 下面的【例一】里CDate类有多少字节?
  • 类的成员函数是如何调用自己的数据成员?
  • 为什么叫this指针,而不是引用呢?
  • this指针有没有可能为NULL呢?

 带着上面四个问题我们来开始探索this指针吧。本文的主要内容如下:

   1、例子引入
   2、【例一】里CDate类有多少字节?
  • 类的存储方式
   3、类的成员函数是如何调用自己的数据成员?
  • C语言模拟实现
  • 编译器如何识别一个类
  • this指针的基本概念
  •       特性
  •       __thiscall调用约定
  • this指针的两种传参方式

   4、为什么叫this指针,而不是引用呢?

   5、this指针有没有可能为NULL呢?


1、例子引入

首先来看一段代码(简化过的,目的只为了解释this指针问题):

【例一】

#include<stdio.h>#include<Windows.h>#include<iostream>using namespace std;class CDate{public:void SetDate(const int year,const int month,const int day){_iYear = year;_iMonth = month;_iDay = day;}private:int _iYear;int _iMonth;int _iDay;}int main(){CDate D1;//对象实例化D1.SetDate(1997,5,1);CDate D2;D2.SetDate(2017,10,24);system("pause");return 0;}

2、【例一】里CDate类有多少字节?

   我们先来看第一个问题,CDate类有多大?可以在主函数里求一下该类的大小,在主函数里添加如下语句:

int len = sizeof(D1);cout<<"CDate类的字节为:"<<len<<endl;
程序运行结果如图所示:



    那么我们可以算一下该类的字节:



2.1 类的存储方式


        到了这里我们可以发现一个有趣的现象,那就是类的成员数据的字节大小与该类的字节大小相同。也就说明类的成员函数并不占用类对象的内存空间,那么类的对象是如何进行存储的呢?我们来看一下图解




   在建立对象时,系统会为每一个对象分配独立的存储空间,而这段空间里只有类的数据成员。也就是说,假如对同一个类定义n个对象,则有n组同样大小的函数时,调用的是同一段函数代码。空间来分别存储各自对象里的数据成员,然后会有一块另外的存储空间,用来放该类的一份成员函数,也就是说,同一个类中的不同对象在调用自己的成员


3.1  C语言模拟实现成员函数调用自己的数据成员


      看到这里就可以引出我们的第二个问题了,那就是既然同一个类的不同对象,共享成员函数的代码,那么调用这个成员函数时,如何确保成员函数里的参数调用的是自己的数据成员而不是别的对象的数据成员呢?

   【例一】里类的成员函数传参只传了三个要赋值的变量,那么编译器怎么知道这个成员函数应该调用那一份数据成员呢?一般C++里的问题我们都可以拿到C语言里来模拟实现,上述成员函数如果用C语言来模拟实现的话,代码如下所示


void SetDate(CDate *D1,const int year,const int month,const int day){      _iYear = year;      _iMonth = month;      _iDay = day;}

         我们发现用C语言来解决这个问题的时候,会有一个这样的指针,用来指向该成员函数所在的对象的起始地址同理,C++中应该也存在一个这样的指针,指向当前被调用的成员函数的所在的对象的起始地址其实,在C++中,每一个成员函数中都包括一个特殊的指针this指针,该指针的名字是固定的,把该成员函数所在的对象的起始地址传递过去。这样就解决了成员函数该调用哪份数据成员的问题了。我们可以测试一下this指针是否存在,我们对【例一】中的成员函数作出如下调整:

void SetDate(const int year,const int month,const int day){cout<<this<<endl;_iYear = year;_iMonth = month;_iDay = day;}


程序运行结果为:



作为对比,可以再看一下类的两个对象的地址:




     我们可以发现this指针确实指向了当前被调用的成员函数的所在的对象的起始地址。因此,我们可以得到结论系统隐式的将对象的起始地址传递给成员函数,使this指针得到当前对象的地址。当不同的对象调用同一个类的成员函数代码时,编译器会依据该成员函数的this指针所指向的不同对象来确定应该引用哪个对象的数据成员


3.2 编译器是如何识别一个类的?

    识别一个类可以分为以下三步:

        a、识别类名

        b、识别类中的成员变量

        c、识别成员函数并对成员函数进行修改

3.3 this指针的基本概念

【this指针特性】
 1、this指针的类型 类类型* const
     2、this指针并不是对象本身的一部分,不影响sizeof的结果。
     3、this的作用域在类成员函数的内部(不严谨)。
     4、this指针是类成员函数的第一个默认隐含参数,编译器自动维护传递,类编写者不能显式传递。


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

【this指针的两种传参方式】
 a、通过ecx寄存器传参(寄存器相当于全局变量)—-当成员函数的参数固定时(这时遵循__thiscall调用约定)。
 b、通过压栈传参—-当成员函数的参数为可变时(这时遵循__cdecl调用约定)。

4、为什么叫this指针而不是引用呢?

 
    因为最开始将c++称为带类的c,而引用是在c++1.0版才加入的,所以叫做this指针。

5、this指针有没有可能为空呢?

 
   有可能,定义一个指向类对象的指针并使其初始化为NULL。举个栗子(修改【例一】中的主函数):

int main(){CDate D1;D1.SetDate(2017,10,25);CDate *pt = &D1;// mov ecx pt;pt->SetDate(1997,10,25);// call CDate::SetDate(1997,10,25);pt = NULL;pt->SetDate(1897,10,25);// 输出为空     CDate::SetDate(pt(NULL));system("pause");return 0;}



   





原创粉丝点击