「这是我参与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();//同上