C++隐藏的this指针(详解)

1 this指针

问题引入:Date类中有Init与Print两个成员函数,函数体中没有关于不同对象的区分,那当d1调⽤Init和
Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?

c 复制代码
#include<iostream>
using namespace std;
class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1,d2;
	d1.Init(2025, 11, 8);
	d2.Init(2025, 11, 9);
	d1.print();
	d2.print();
	
	return 0;
}

不同的对象本质就是它们的地址不同,那函数区分这两个对象也一定根据它们的地址进行区分。

可是我们也看到我们调用函数的时候并没有传入各对象的地址啊,其实这里就是C++的隐含的this指针来解决。

在编译器编译后 ,类的成员函数都会在形参的第一个位置增加一个当前类类型的指针,叫做this指针。eg:void Init(int year, int month, int day)--被编译器替换为--void Init(Date* const this, int year, int month, int day)

而我们调用成员函数时便是eg:d1.print()--被编译器替换为--d1.print(&d1),我们在调用函数的时候就将当前对象的地址 传递给了this指针,从而来确定函数调用对象的。

在类的成员函数中访问成员变量本质也是通过this指针访问的

c 复制代码
void Init(int year, int month, int day)
{
	_year = year;//等价于this->_year = year;
	_month = month;
	_day = day;
}

小结:访问类的成员函数和成员变量都是通过this指针确定对象的

2 this指针位置的限定

C++规定this指针不能在实参和形参的位置显示写 ,在形参和实参位置由编译器在编译时候处理 ,只有在成员函数内部可以显示运用this指针

在实参和形参的位置报错:

我们也可以这样想,编译器会固定帮你默认添加 ,你自己再显示添加在实参位置(函数调用参数太多 ),形参位置(函数调用参数太少)。

在成员函数内部:

我们看到可以正常使用

this指针内存区域说明:我们也看到this指针就是函数的参数 ,所以this指针是存在栈区 的,不过有时候编译器为了加快速度也会将其放入寄存器 之中

这个汇编指令便是将this指针变量所存储的值放入rax寄存器中。

3 this指针类型说明

我们可以看到this指针指向不能更改,但其指向的空间内容可以修改。所以this指针是一个指向被const修饰的指针,在Date类中就是Date * const this

两道判断题

接下来我们通过两道判断题进一步的理解this指针

1.下面程序编译运行结果是()

编译报错/运行崩溃/正常运行

c 复制代码
#include<iostream>
using namespace std;
class A
{
public:
	void Print()
	{
		cout << "A::Print()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

这段代码实际上被正确的执行了,接下来我们详细的分析下

在上面代码中其实最让人迷惑的就是p->Print(),这里好像是空指针的解引用啊,这难道不会运行崩溃嘛?

实际上面对p->Print()这个语句编译的时候做了两件事

  • 找到Print函数的出处(声明或者定义),调用一个函数被编译后在汇编指令上就是call 函数地址
  • 对象或者对象指针调用在编译处理后都传递this指针

本题在Print函数内部没有对this指针(nullptr)的解引用,所以不会有任何的错误

但是如果在Print函数内部访问成员变量则会运行崩溃

小提醒:编译报错一般是语法出现了问题,对空指针解引用并不是语法层面的问题,在编译的时候编译器只知道这个是一个指针解引用符合语法,只有在运行的时候才知道这是个nullptr才报错

1.下面程序编译运行结果是()

编译报错/运行崩溃/正常运行

c 复制代码
#include<iostream>
using namespace std;
class A
{
public:
	void Print()
	{
		cout << "A::Print()" << endl;
		//cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	(*p).Print();
	return 0;
}

这个题目答案也是正常运行
p为nullptr,空指针的解引用并不会触发语法编译的问题,那只有在运行的时候才会报错。

可是在运行的时候编译器是按照你的代码逻辑转化为对应指令的而不是看到*p就一定有执行*p的指令,具体看代码实际表达的实际意义是什么。

在这个代码中*p的指令存在与否不会影响程序的实现,所以根本就没有*p的指令,所以代码正常运行

我们可以清楚的看到汇编指令上,在Print函数调用方面没有区别,更加证实了我们的说明

相关推荐
wo_jack4 小时前
C++ 入门笔记(十七):list 入门 —— 为什么 list 不能 sort?
数据结构·c++·面试·stl·list
青少儿编程课堂5 小时前
蓝桥杯青少组图形化 + 全国青少年信息素养大赛 + 电子学会Scratch考级:校园连连看解析
c++·python·算法·bfs·信息学竞赛
草莓熊Lotso5 小时前
【C++ 基于微服务的即时通讯系统】文件存储子服务实战:UUID生成、文件IO封装与全接口单元测试
开发语言·网络·数据库·c++·微服务·架构·单元测试
David猪大卫1 天前
【C++修炼】map 和 set 的使用
c语言·开发语言·数据结构·c++·经验分享·笔记·学习
HPtiger1 天前
C++ 入门笔记:从 C 到 C++ 的核心过渡(命名空间、输入输出、缺省参数、重载、引用、内联)
c语言·c++·学习
努力努力再努力wz1 天前
【Lua 进阶系列】从 C++ 对象到 Lua 变量:一文理解 userdata、Lua 栈与元表绑定机制
c++·junit·lua
慧都产品智能化1 天前
Qt 表格升级攻略:复用已有编辑器,减少重复开发
c++·qt·编辑器·qt6.3
ShineWinsu1 天前
对于Qt:QTableWidget常见属性以及QTableWidgetItem类的解析
开发语言·c++·qt·gui·客户端·表格·qtablewidget
ShineWinsu1 天前
对于Qt:QListWidget常见属性以及QListWidgetItem类的解析
开发语言·c++·qt·客户端·列表·qlistwidget·qlistwidgetitem
坤坤子吖1 天前
Linux网络编程:自定义应用层协议与 JSON 序列化
linux·服务器·网络·c++·笔记·json