小伙伴大家好,今天我们将在这篇文章中讨论一个有趣的知识点--隐藏的this指针。本篇需要用到前面学的C++类和对象(上)的基础,如果大家还不太了解类和对象的基本思想,可以去回顾一下这篇文章【C++】带你一篇了解什么是OPP(面向对象编程),什么是封装?类和对象(上)。
接下来我们学习一个时间类,顾名思义,就是会输出我们输入的时间,我们先看一下这段代码:
class Date
{
public:
void Display()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
void SetDate(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
int main()
{
Date d1, d2;
d1.SetDate(2025, 7, 15);
d2.SetDate(2025, 7, 16);
d1.Display();
d2.Display();
return 0;
}运行结果:

我们通过汇编代码来看一下两次调用的函数是不是同一个(我们之前讲过类对象的储存方式:只储存成员变量,成员函数放在公共代码段):

我们看到两次调用的函数的地址是一样的,那么大家思考一下,既然调用的函数是一样的,那么编译器为什么知道d1是2025-7-15。而d2是2025-7-16呢?
这是因为C++在处理这种情况的时候偷偷地做了手脚,添加了一个this指针,在这里就是Display()函数传入了一个this形参。
事实上,C++会为所有非静态的成员函数增加了一个隐藏的this指针,这个指针用来指向调用当前函数的对象,而对于类中所有成员变量和成员函数的访问,都是通过这个this指针。只不过这些操作对于用户来说是隐藏的,由编译器自动完成。
我们将代码补全来解释可能会清晰一点(注意:代码补全是为了方便理解,不一定满足语法规则):



这样理解是不是就会好一些,也就是在调用成员函数时候都会往里面传入了一个指针,指向的是当前对象。但是再次提醒,我们不能显式地写出来,不能抢了编译器的活。
在真正的编译器中,this使用const修饰的,也就是说
![\left [ 1 \right ]](https://developer.qcloudimg.com/http-save/yehe-100000/ea68e00825e69038c63b3eacab859c62.png)
this本身是无法被修改的,但是它的内容可以被修改,
![\left [ 2 \right ]](https://developer.qcloudimg.com/http-save/yehe-100000/51f0e1d23e4a5b116d102d9ce5da46c9.png)
并且我们是可以直接使用的:
void Display()
{
cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
}我们可以来打印一下this指针,还有d1、d2:
class Date
{
public:
void Display()
{
//使用this指针
cout << this << endl;
cout << _year << "-" << _month << "-" << _day << endl;
}
void SetDate(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
int main()
{
Date d1, d2;
cout <<"d1:"<< & d1 << endl;
cout <<"d2:"<< & d2 << endl;
d1.SetDate(2025, 7, 15);
d2.SetDate(2025, 7, 16);
d1.Display();
d2.Display();
return 0;
}运行结果:

并且我们刚才讨论过了,这个this指针是真实存在的,所以我们是可以直接使用的,当然我并不需要显示地传入参数,直接直接调用即可:
void Display()
{
cout << this << endl;
cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
}然后我们来讨论一下this指针的不可修改性,我们看下这段代码能不能编译成功:
void SetDate(int year, int month, int day)
{
this = nullptr; //将nullptr赋值给this
_year = year;
_month = month;
_day = day;
}很显然是错误的,因为前面提到了this是被const修饰的,是不允许被修改的。如果编译的话会爆出这样一个错误:

总结:
(1)下面的程序的运行结果是? A.编译报错 B.运行崩溃 C.正常运行
class A
{
public:
void Show()
{
cout << "show()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Show();
return 0;
}答案:C
因为之前在讲解类和对象的时候我们讲过,成员函数是储存在公共代码区,调用的时候只需要call函数的地址就行。而虽然我们这里this被赋值了nullptr,也就是this被初始化为空指针而已,只是被允许的,我们看一下运行结果和反汇编结果:


(2)下面的程序的运行结果是? A.编译报错 B.运行崩溃 C.正常运行
class B
{
public:
void PrintA()
{
cout << _a << endl;
}
private:
int _a;
};
int main()
{
B* p2 = nullptr;
p2->PrintA();
return 0;
}答案:B
大家可能有疑问,这不是跟上面一个类型的题目吗,为什么上面那个能够正常运行,这个就会运行崩溃呢?大家注意这两道题的区别,第二道题中存在对成员变量的访问(即this->_a),而成员变量的访问是需要获取对象的地址的(也就是this指针),但是这里的this指针被初始化成了空指针(因为p2被赋值了nullptr),所以这里就变成了对空指针的解引用,自然会报错:

(3)this指针是存在哪里的?A.栈 B.堆 C.静态区 D.常量区
答案:A
解释:
this指针是个形参,形参是在函数的栈桢里,在函数的栈桢里面的变量是属于栈中的。
有时候编译器会使用寄存器对其进行优化,将this指针存放在寄存器中:

下面这图可以更直观的看出涉及的变量的储存位置:

(本篇完)