1、引出this指针
首先我们来创建一个日期类Date:
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(2024,1,27);
d2.Init(2024, 2, 28);
d1.Print();
d2.Print();
return 0;
}
代码执行结果如下:
看到运行结果可能会有一个疑问:d1,d2都是调用了同一个函数Print,可是为什么结果会不一样呢?当d1调用Print函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
ps:首先我们要明确一点:Print函数中打印的年月日不是private的年月日,上篇博客分享到的类的实例化提到过,上述private中的只是声明,没有独立的内存空间。
编译时像Print这样的成员函数要去找其定义的位置,一般先去局部找,再去全局找,有命名空间可以去命名空间找。但在这里似乎都找不到,所以就自然引出(隐含的)this指针。(被当作成员函数的第一个参数),可以理解成编译器处理后变成以下的样子:
调用这个函数的地方编译器也会处理成:
将其当作形参来使用,也就形成了以下的调用模式:
把对应的地址传过去,相当于指针的访问。
2、this指针的特性(总结)
1.this指针的类型: const*,即成员函数中,不能给this指针赋值。(这里回忆下C语言const用法:const位于 * 前,表示指针指向的内容不可修改;const位于 * 后,表示指针本身不能被修改)
2.this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给
this形参。所以对象中不存储this指针。
3. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传
递,不需要用户传递。
3、this指针相关面试题节选
a.this指针存在什么地方?
this指针会因编译器不同而有不同的放置位置。可能是栈,也可能是寄存器,甚至全局变量。
b.this指针可以为空吗?
可以为空(分情况的),我们在调用函数的时候,如果函数内部并不需要使用到this指针,也就是不需要通过this指向当前对象并对其进行操作时才可以为空;
如果调用的函数需要指向当前对象,并进行操作(就像上述d1,d2调用函数打印不同日期时的情况),则会发生错误(空指针引用)就跟C中一样不能进行空指针的引用。
举两个例子:
一:
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
这里就验证了第一种this指针可以为空的情况(没有对空指针解引用),运行结果如下:
二:
class A
{
public:
void PrintA()
{
cout<<_a<<endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
return 0;
}
第二种情况(运行崩溃):
空指针的传递没有问题,只有当访问的时候才会崩溃。
额外补充:
int main()
{
A* p = nullptr;
/*p->PrintA();*/
(*p).PrintA();
return 0;
}
这种情况又该如何呢?
class A
{
public:
void PrintA()
{
cout << this << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
(*p).PrintA();
return 0;
}
PrintA不是存在于对象当中,传递this指针不需要* p,编译器会根据自己的需要去解引用,要去看解引用之后有没有意义(需不需要去对象指向的空间找东西)
根据汇编代码我们也可看出 (*p).PrintA()和p->PrintA()形式相同:
好了,这篇博客我们就聊到这个地方了,下篇博客见了朋友们~