【C++】this指针讲解超详细!!!

在这里插入图片描述

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤
📃个人主页 阿然成长日记 👈点击可跳转
📆 个人专栏: 🔹数据结构与算法🔹C语言进阶
🚩 不能则学,不知则问,耻于问人,决无长进
🍭 🍯 🍎 🍏 🍊 🍋 🍒 🍇 🍉 🍓 🍑 🍈 🍌 🍐 🍍

一、this指针引入

1.引入

#include<iostream>
using namespace std;
class Data
{
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()
{
//创建Data的对象1
	Data d1;
	d1.Init(1999, 1, 1);
//创建Data的对象2
	Data d2;
	d2.Init(2023, 1, 1);
//调用print函数
	d1.print();
	d2.print();
	return 0;
}

打印结果:
在这里插入图片描述

2.分析

上面代码运行结果中,为什么两个对象调用同一个函数能打印出不同的日期呢?或者说同一个函数如何做到结果不同呢?
简单说是通过C++的一个关键字this来实现的。
上面代码着编译后,就成了如下:

#include<iostream>
using namespace std;
class Data
{
public:
	void Init(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void print(Data* this)
	{
		cout << this->_year << ":" << this->_month << ":" << this->_day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
//创建Data的对象1
	Data d1;
	d1.Init(1999, 1, 1);
//创建Data的对象2
	Data d2;
	d2.Init(2023, 1, 1);
//调用print函数
	d1.print(&d1);
	d2.print(&d2);
	return 0;
}

当不同对象调用print函数时,其实在底层是传了一个Data*类型的指针this。

void print(Data* this)
	{
		cout << this->_year << ":" << this->_month << ":" << this->_day << endl;
	}

> 如下图所示:
其实底层还是和C语言函数调用一样,也需要传参,只不过C++的编译器为我们默默做了这些事情,让我们用起来更方便。但是,不能显示的去写出this的相关实参和形参,需要注意的是在类中可以使用this->。

3.图示

在这里插入图片描述

this本质是一个形参,哪个对象调用他,就指向哪个对象。

二、 this指针存在哪里?

下图是代码在vs2019编译器下执行的汇编语句
在这里插入图片描述
当执行d1.Init(1999, 1, 1);时,先是将三个实参进行压栈。其实实参应该有4个,还有一个隐藏的this指针
(1)000426EF push 1
(2)000426F1 push 1
(2)000426F3 push 7CFh
通过这一句(4)000426F8 lea ecx,[d1] 我们可以清晰的看到this指针在vs编译器下,是存到了寄存器( ecx)。
其实,this指针存在的位置一般是在栈帧上,不同的编译器有所不同,例如vs2019就是存在寄存器上。
因为this指针,占用内存小并且需要频繁使用,所以,将它放到寄存器里能大大提升运行效率。

三、this指针的特性

  1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
  2. 只能在“成员函数”的内部使用
  3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给
    this形参。所以对象中不存储this指针。
  4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传
    递,不需要用户传递

四、 案例分析

下面代码会出现问题吗?

class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}

前提知识

  • p的意义有两个。1.p是A类一个对象的指针,p->print(),对象的指针调用函数肯定是找成员函数中的,也就是说编译的时候检查语法,让我们知道print函数是A类中的成员函数,起到定位语法作用。

  • p指针为空,相当于this指针为空。

  • 成员变量存储在对象中

  • 成员函数没有存储在对象中

  • p->Print();说明调用的是A类中的成员函数

调用print函数与this指针无关,只有访问成员变量时才会使用到this。
因为函数是在编译链接的时候找的(本质是找函数的地址),上一篇详细讲过对象的存储方式中讲过,成员函数存在公共区域的函数表(函数表中存有成员函数的地址)查找修饰后的函数名【因为C++支持重载,所以需要对函数名 进行特殊的修饰】来进行查找。

所以,代码运行正常。
看下面代码:

class A
{
public:
void PrintA()
{
cout<<_a<<endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
return 0;
}

printA函数中使用了成员变量,编译时printA函数底层如下形式:

void PrintA(Data* this)
{
cout<<this_a<<endl;
}
mian函数中是
p->PrintA(p);

将p传给了this指针。
_a=>this_a,需要访问this指针,但此时this指针为空。
所以,运行崩溃!

评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿然成长日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值