this指针

143 阅读3分钟

「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战

💦 this指针的引出

class Date
{ 
public:
	//void Display(Date* this)
	void Display ()
	{
		cout <<_year<< "-" <<_month << "-"<< _day <<endl;
	}
	//void Init(Date* this ,int year , int month , int day)
 	void Init(int year , int month , int day)
 	{
		_year = year;//this->_year = year;
 		_month = month;//this->_month = month;
 		_day = day;//this->_day = day;

		Display();//this->Display(&d1/&d2);
 	}
private:
 	int _year ; //年
 	int _month ; //月
 	int _day ; //日
};
int main()
{
	Date d1, d2;
	d1.Init(2021,1,10);//d1.Init (&d1, 2021, 1, 10);
 	d2.Init(2022,1,10);//d2.Init (&d2, 2022, 1, 10);
 	d1.Display();//d1.Display(&d1);
 	d2.Display();//d2.Display(&d2);
 	return 0;
}

📝 说明:

我们经常会在 C++ 代码里看到变量前加一个杠 _ 或者在变量后加一个杠 _,这其实是 C++ 的规范用法, 这里的价值在如上代码就体现了 —— 如果不加杠,在初始化时就懵了 (year = year),加杠就代表它是成员变量

❓ 这里 d1 和 d2 是两块不同的空间,它们分别调用 Init 时,程序是怎么知道 _year、_month、_day 是 d1 还是 d2 ❔

我们说了这个函数不是对象里面的,它们是同一个函数,它是怎么知道 d1、d2 呢 (调试发现 d1 和 d2 是两块不同的空间) ?—— 运行程序,发现 C++ 就是区分唉!—— C++ 是怎么区分的 ?

其实编译器在 d1.Init (2021, 1, 10); 时会把 d1 的地址也传参 d1.Init (&d1, 2021, 1, 10);

而形参对应的变成了 void Init (Date* this ,int year , int month , int day)

且 _year、_month、_day 是声明,不能赋值,所以这里变成了 this->_year、this->_month、this->_day

注意 this-> 站在自己的角度可以把它补充出来,你写了编译器就不加,你不写编译器就加。但是你不能在实参和形参上补充,这是编译器干的活你不能强抢

是否可以在 Init 里调用 Display ❓

成员函数里面,我们不加的话,默认会在成员前面 + this->

this->Display() ;

对象里可以调用成员函数,成员函数里还可以调用成员函数,因为有 this 指针

💦 this指针的特性

1️⃣ this 指针的类型:类类型* const。

2️⃣ 只能在 “成员函数” 的内部使用。

3️⃣ this 指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给 this 形参。所以对象中不存储 this 指针。

4️⃣ this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。

【面试题】

1、this 指针存在哪里 ?(堆 | 栈 | 静态区 | 常量区)

常犯的错误:this 指针存在对象里面 !!!

this 指针是形参,形参和函数中的局部变量都是存在函数栈帧里的,所以 this 指针可以认为是存在栈的。

VS 下为了提高效率,this 指针是通过寄存器 ecx 传递的。

验证 ❓

调试代码后转到反汇编 在这里插入图片描述 2、this 指针可以为空吗 ?

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

	A* q = nullptr;
	q->Print();//q->Show(q);
	return 0;
}

❓ 上面程序的运行结果是什么 ❔

A. 编译不通过

B. 运行崩溃

C. 运行通过

📝 分析:

p->Show() ;

成员函数的地址不在对象中存储,存在公共代码段。这里调用成员函数,不会去访问 p 指向的空间,也就不存在空指针解引用了,这里只会把 p 传递给隐含的 this 指针,但是 Show 函数中也没有解引用 this 指针。所以这里选择 C 选项。

q->Print() ;

这里同上,Print 不在 p 里面,它在公共代码段,然后把 q 传递给隐含的 this 指针,至此没问题。但是在 Print 函数里访问成员变量时 _a 时会补充上 this->_a;,而这个指针是一个空指针,所以崩了。所以这里选择 B 选项。

易混淆:

A aa;
A* p = &&aa;

p->_a;//去p指向的空间访问_a,所以这是一个解引用行为
p->Show();//Show不是存在p指向的对象里,而是存在公共代码段,所以这里不是解引用。这里是把p传递给形参this*
aa.Show();//同上