本篇文章我们将一起讨论在有趣的知识点--隐藏的this指针。本篇我们要使用到之前我们所学习到的C++类与对象(1),如果有各位小伙伴还不曾了解类与对象的简单思想,可以访问上篇博客:[ C++ ] 带你一篇了解什么是OOP(面向对象编程),什么是封装? -- 类与对象(上)
目录
1.this指针的引出
2.this指针的特性
3.练习一下
在之后的学习中,我们将认识一个新的类:日期类Date。正如我们所想的那样,传入一个日期,我们可以输出我们所输入的日期。
那我们首先来看一下,这段代码会输出什么结果呢?
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(2022, 5, 11);
d2.SetDate(2022, 5, 12);
d1.Display();
d2.Display();
return 0;
}
输出结果:
我们首先可以通过汇编来看看,d1,d2调用的函数是否相同。
我们可以发现,最终打印的时候调用的Display()是同一个函数, 那么既然d1,d2调用的都是同一个函数,编译器如何知道d1是2022-5-11,d2是2022-5-12呢?Display()都访问的_year,_month,_day。而且去公共代码区访问的Display(),这是为什么呢?
这是因为C++在这段代码中做出手脚,C++在这里增加了一个this指针,这里是因为Display会增加一个this形参。C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
在调用的时候也传的是各自的地址。这样就十分清晰明了了。这就是隐含的this指针
注意:我们不能显示的写出来,因为他是隐含的,我们不能抢了编译器的活。但是我们可以直接在类里面用。
在真正的编译器中this指针的用const修饰的,this指针本身是不能被修改的,但是内容是可以修改的。并且我们是可以使用的
我们可以在类中打印一下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(2022, 5, 11);
d2.SetDate(2022, 5, 12);
d1.Display();
d2.Display();
return 0;
}
运行结果:
并且我们还能这样写,但是我们不能显示的写出Date* this。
我们接下来再看看this指针是不能修改的,大家看下面这个能过吗?答案肯定是不能的,因为this是被const修饰的,不能修改this指针的。
我们会发现 编译器也会报错:error C2106: “=”: 左操作数必须为左值
this指针的特性总结:
1. this指针的类型:类类型* const。2. 只能在“成员函数”的内部使用。3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
(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
原因:Show()函数是存在公共代码区中,编译的时候在公共代码区中找到这个函数,和普通的函数调用是一样的,只需要call函数地址就行。我们发现这里p是空指针,传过去的this指针只是接收了p的空指针,就类似于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
原因:此程序崩溃是在PrintA()中,会隐含一个this->_a,而this指针是一个空指针,访问this指针_a的位置,就要对空指针进行解引用,此时就会崩溃。我们也可通过调试观察到。
(3)this指针是存在哪里的?
a.栈 b.堆 c.静态区 d.常量区
答案:a
解释:this指针是个形参,形参是在函数的栈桢里,在函数的栈桢里面的变量是属于栈中的。
有时编译器会使用寄存器对其进行优化,this指针会存在寄存器中。
(本篇完)