前言
我们在学习类的过程中,一定听说过this指针,但是并不知道它跟谁相似,又有什么用途,所以接下来,让我们一起去学习this指针吧!
一、this指针的引入
我们先来看下面两段代码,它们输出的是什么?
#include <iostream> using namespace std; class Date { public: Date(int year, int month, int day) { _year = year; _month = month; _day = day; } void Printf() { cout << _year << '-' << _month << '-' << _day << endl; } private: int _year; int _month; int _day; }; int main() { Date date1(2023,11,14); date1.Printf(); Date date2(2022,11,14); date2.Printf(); return 0; }
我们可以明显的看到输出的结果,分别是我们输入的年月日,也就是说我们的类的成员变量并不是一个,而是多个,或者说是我们定义了几个实例化对象就有几个成员变量。
那为什么在C++中可以自动区分实例化对象的成员变量呢?他们是靠什么区分的呢?
其实这个就类似我们的C语言了,如果这个程序让C语言来写就是这样的:
我们会发现,在C语言中要完成这样的操作,是需要提供我们这个实例化对象(date1,date2)的地址的,只有提供它们对应的地址,才可以完成专属的初始化,打印等操作。
#include <stdio.h> typedef struct Date { int _year; int _month; int _day; }Date; void Init(Date *pd,int year, int month, int day) { pd->_year = year; pd->_month = month; pd->_day = day; } void Print(Date *pd) { printf("%d-%d-%d\n",pd->_year,pd->_month,pd->_day); } int main() { Date date1; Init(&date1,2023,11,14); Print(&date1); Date date2; Init(&date2,2022,11,14); Print(&date2); return 0; }
所以我们的C++其实也是根据传入每个实例化对象的地址,再用指针变量来接收的,只不过这部分操作是让编译器负责,不用咱们操心了,这就是大名鼎鼎的this指针。
二、this指针
this指针就是存我们实例化对象的地址,之后根据每个实例化对象的地址来进行专属的操作的。我们的成员函数其实比我们看到的形参都多一个隐藏的参数this,所以正是基于这个原因this是一个关键字,在C++中被规定就是指向实例化对象的地址的。
它相当于下面这样:
但是this指针不会在成员函数的形参中直接出现,这也就说明了,我们也不需要传实例化对象的地址,因为这都是编译器默认来完成的事情,我们做了就属于画蛇添足。
那this指针可以在哪里出现呢?
this指针可以在成员函数的内部出现,这是被允许的。我们可以这么使用this,但是这也仅仅是帮助我们来理解为什么可以找到实例化对象对应的成员变量的,后期我们熟悉了,就不需要了。
Date(int year, int month, int day) { this-> _year = year; this->_month = month; this-> _day = day; } void Printf() { cout << this-> _year << '-' << this-> _month << '-' << this-> _day << endl; }
三、this的常见问题
第一个问题:this可以被改变吗?
答案:不可以,默认this类型为:类类型 *const
解析:
因为我们的this指针,是实例化对象的地址,所以一般是不会改变它的,所以为了防止我们瞎玩,编译器默认this指针的类型是 类类型 *const this
const 在 * 之前,是修饰的指针指向的对象,指针可以改,指向的对象不可以被修改
const在 * 之后,是修饰的指针本身,也就是指针不能修改,但是指向的内容可以修改第二个问题:this可以在哪里使用?
答案:只在成员函数中使用
解析:
很明显,this是成员函数都有的一个形参,所以属于的是成员函数的作用域内,仅仅可以在自己的成员函数中使用
第三个问题:this存放在哪里?
答案:存放在函数栈帧上,也有的存放在寄存器上,主要看编译器
解析:
因为this指针属于形参,所以就跟局部变量一样,是在函数栈帧上面开辟的空间,存在栈帧上的
但是有的编译器是用寄存器存的,因为this会被编译器默认认为是经常使用的,又因为存在寄存器上很快,所以就存在寄存器上了。
不同的编译器存放this指针的地方不同
第四个问题:this可以为空吗?
答案:单纯的对this赋空是不可以的,不过可以强转直接赋空,不过一般不进行这样的操作
解析:我们看下面的代码来分析:首先是正常运行的
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行 #include <iostream> using namespace std; class A { public: void Print() { cout << "Print()" << endl; } private: int _a; }; int main() { A* p = nullptr; p->Print(); return 0; }
下面的代码我们定义了一个实例化对象的指针,指向的是空,但是这个是正常运行的,因为虽然我们的实例化对象的地址为空,可是在程序里我们访问的是成员函数,又因为成员函数的地址不存放在实例化对象里,所以根本没有空指针的解引用,是正常运行的;
但是如果是下面这样的代码,就会运行错误了
// 2.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行 #include <iostream> using namespace std; class A { public: void Print() { cout << _a << endl; } private: int _a; }; int main() { A* p = nullptr; p->Print(); return 0; }
为什么不是编译错误,原因就是编译阶段只是检查我们的语法问题,无法识别空指针的解引用错误。运行错误就是因为在成员函数Print中,我们解引用了this指针,_a 相当于 this->_a 。所以这就是对空指针的解引用,原因是成员变量存放在实例化对象中
编译链接的博客在这里:http://t.csdnimg.cn/L6jFl
第五个问题:this指针出现的场景
答案:目前作者学习用到this的地方是在运算符重载这里,等以后学习更多知识会更新的
例子:
我们以日期类为准,想比较日期的大小,肯定是不能直接比较的,因为日期类是一个自定义类型,所以我们要用到运算符重载,在大于,等于这里我们就正常比较,但是到了小于,我们可以复用前面的,因为小于就是不大于,不等于,但是如何找到当前的日期呢?因为我们知道成员函数都有一个隐藏的参数--this指针,所以我们可以通过this指针来找到比较的其中一位,另一位就是another,比较如图:
class Date { public: //运算符重载 > bool operator>(const Date& another) const; //运算符重载 == bool operator==(const Date& another) const; //运算符重载 < private: int _month; int _day; int _year; }; // //运算符重载 > bool Date::operator>(const Date &another) const { if(_year > another._year) return true; else if(_year == another._year && _month > another._month) return true; else if(_year == another._year && _month == another._month && _day > another._day) return true; else return false; } // //运算符重载 == bool Date::operator==(const Date &another) const { return _year == another._year && _month == another._month && _day == another._day; } // //运算符重载 < bool Date::operator<(const Date &another) const { return !(*this > another || *this == another); }