C++学习系列(4)——深入理解C++类成员函数的this指针

类成员函数的this指针

前言

因为目前很多小伙伴对类的构造函数不是很理解,为了大家便于理解我这里暂且使用成员函数对类成员变量赋初值,下一篇博客我会详细介绍构造函数,敬请期待!最后本人能力有限,如果文章有出现错误的地方欢迎指出。

一、this指针的引出

下面我们先看这段代码:

class Date
{

public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void func()
	{
		cout << this << endl;
		cout << "func" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2;
	d1.Init(2023, 7, 10);
	d2.Init(2022, 5, 14);
	d1.func();
	cout << "----------------------------" << endl;
	d2.func();
	return 0;
}

运行结果:
在这里插入图片描述
对于上述类,有这样一个问题:
问题:Date类函数体中没有关于不同对象的区分,那当d1调用Init函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
解答:C++通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数”(静态成员函数没有this指针)增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户都是透明的,即用户不需要来传递,编译器自动完成。
我们也可以从上面的运行结果中确实每个成员函数都有一个隐藏的this指针,并且不同对象里成员函数的this指针也不一样
下面我将类里面的this指针标出,大家可以以此借鉴。

class Date
{
public:
	void Init(Date* this, int year, int month, int day)
	{
		//因为这里
		this._year = year;
		this._month = month;
		this._day = day;
	}
	void func(Date* this)
	{
		cout << this << endl;
		cout << "func" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

二、 this指针的特性

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

  1. this指针存放在哪里?
    this指针是成员函数的隐含形参,所以存放在栈上/ vs下是通过ecx寄存器
  2. 空指针问题
class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void func()
	{
		cout << this << endl;
		cout << "func" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date* ptr = nullptr;
	ptr->Init(2022, 2, 2);//运行崩溃
	ptr->func(); //正常运行
	(*ptr).func;
	return 0;
}

解释
问题提出:为什么会出现上面的两种不一样的结果呢?同样是拿空指针调用类里面的成员函数,一个是运行崩溃,一个是正常运行。这里我们要分成两点来解释。
解答:我们知道空指针解引用会报错,那为什么ptr -> func()不会报错呢?这里需要提出一个点,就是并不是我们在写代码的时候编写了解引用的代码,编译器在编译阶段就会将指针解引用,我们知道这里ptr -> func()是调用类里的成员函数,所以会将对象的地址作为实参传递给this形参,然而在func()函数中并没有对this指针的解引用,所以这里并不会报错,其实它的含义仅仅是将对象的地址作为实参传递给成员函数的this指针。因此我如果将Init()函数也稍加修改,那么就不会运行崩溃了。

class Date
{
public:
	void Init(int year, int month, int day)
	{
		cout << this << endl;
		/*_year = year;
		_month = month;
		_day = day;*/
	}
	void func()
	{
		cout << this << endl;
		//cout << "func" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date* ptr = nullptr;
	ptr->Init(2022, 2, 2);
	ptr->func(); 

	return 0;
}

运行结果:
在这里插入图片描述
我们发现现在代码可以编译运行了,并且打印出了我们传递进去的ptr的地址。
那我们现在再来看看,如果Init()函数如果不改,是在哪里发生编译错误。
下面是调试的图片:
在这里插入图片描述
我们发现其实并没有在ptr解引用的地方报错,反而是在Init()函数里面因为 this 指针为空指针解引用报错,因此也就证明了我们的猜想是正确的,当对象在调用成员函数的时候,并不会发生解引用,而是将对象的地址作为实参传递给成员函数的this指针

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值