前言
- 🚄 输入是学习的本质,输出是学习的手段。
- 🔖 分享每一次的学习,期待你我都有收获。
- 🎇 欢迎🔎关注👍点赞⭐️收藏✉评论,共同进步!
- 🌊 “要足够优秀,才能接住上天给的惊喜和机会”
- 💬 博主水平有限,如若有误,请多指正,万分感谢!
有这样一个人,它任劳任怨,它默默承受一切,帮我们打点好一切,但却不愿意将自己摆在明面上。
对在座的程序员们都无私奉献,甚至我们要将它堂堂正正地声明出来,都会拒绝我们(指报错)。只愿意做各位老铁成功背后的’‘指针’‘,堪称现代版海螺姑娘,这个人就是——>this指针!!!
this指针
class Data
{
public:
void Init(Data* this, int year, int month, int day)
{
year = year;
month = month;
day = day;
}
private:
int year;
int month;
int day;
};
int main()
{
Data d1;
d1.Init(2022, 1, 1);
}
这段代码中,函数的形参名与成员变量的名字相同,当我们调试的时候,可以看到,成员变量的值并没有被改变。
这是由于编译器并不能区分,你到底要的是成员变量还是形参,因此遵循就近原则,等号左右两端,都是形参。
如果我们要说明是形参赋值给成员变量,有三种方法。
- 声明作用域,告诉编译器,等号左端的变量是类成员变量。
class Data
{
public:
void Init(int year, int month, int day)
{
Data::year = year;
Data::month = month;
Data::day = day;
}
private:
int year;
int month;
int day;
};
这种方法虽然能得到正确结果,但是麻烦,不实用。
- 修改成员变量名称或形参名称,这是最有效、方便避免产生歧义的方法。
我们可以养成自己的代码风格,比如在成员变量前后加上短横线"_",用来说明它是类的一部分,也能起到区分的作用。
class Data
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
- 用this指针
class Data
{
public:
void Init(int year, int month, int day)
{
year = year;
month = month;
day = day;
}
private:
int year;
int month;
int day;
};
int main()
{
Data d1;
Data d2
d1.Init(2022, 1, 1);
d2.Init(2022, 1, 2);
}
成员函数是存储在公共代码区中,那么两个对象在调用同一个函数的时候,不怕两次调用改变的是同一个对象而不是两个对象吗?怎么区分开来?
编译器在编译成员函数的时候,会增加一个隐含的参数——Data * this
this是C++中的关键字,只有一个作用,那就是作为成员函数的隐含参数
实际上编译时代码是这样的
class Data
{
public:
void Init(Data* this, int year, int month, int day) //隐含的this指针
{
year = year;
month = month;
day = day;
}
private:
int year;
int month;
int day;
};
int main()
{
Data dt;
d1.Init(&d1,2022, 1, 1); //编译时自动添加地址
d2.Init(&d2,2022, 1, 1); //编译时自动添加地址
}
在对象调用函数时,编译器会隐含地将对象的地址也传给函数,并在函数中隐含地添加this指针用来接收。
注意:这里一直在强调 “隐含地添加”,
这里是为了方便展示,才明确地将d1的地址写在函数的调用里,将this指针写在形参中。
但实际上我们不能越俎代庖,不能自己在调用函数时传对象的地址,不能自己在函数形参中添加this指针。
这是编译器去做的事,如果我们把它明确地写出来了,
编译就会报错。但是我们可以使用this指针。
我们可以利用this指针来说明等号左边的变量是对象的成员变量,即:
class Data
{
public:
void Init(Data* this, int year, int month, int day) //隐含的this指针
{
this->year = year;
this->month = month;
this->day = day;
}
private:
int year;
int month;
int day;
};
int main()
{
Data dt;
d1.Init(2022, 1, 1); //编译时自动添加地址
d2.Init(2022, 1, 1); //编译时自动添加地址
}
通过取不同名称避免形参与类成员变量的名字相同时,编译器也是默认添加this指针的,只是它帮我们隐藏省去了。即,在调用函数时,都是通过this指针去访问成员变量的。
class Data
{
public:
void Init(Data* this, int year, int month, int day) //隐含的this指针
{
this->_year = year;
this->_month = month;
this->_day = day;
}
private:
//名称不同
int _year;
int _month;
int _day;
};
int main()
{
Data dt;
d1.Init(2022, 1, 1); //编译时自动添加地址
d2.Init(2022, 1, 1); //编译时自动添加地址
}
总结:
this指针的特性
this指针的类型:类类型* const
只能在“成员函数”的内部使用
this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
调用函数时取对象的地址,通过寄存器传递。
- this指针存在哪里?
this指针虽然是个隐含的参数,但是毕竟也是属于函数的一部分,因此和函数一样,this指针也是存储在栈帧中的。
- this指针可以为空吗?
可以
我们看这一段代码,会崩溃吗
class Data
{
public:
void Print()
{
cout << "Print()" << endl;
}
void Year()
{
cout << "year = " << _year;
}
private:
int _year;
};
int main()
{
Data* p = NULL;
p->Print(); // 1.
p->Year(); // 2.
}
这里可能会产生疑惑:
p是空指针,空指针怎么能解引用,所以程序全崩。
因为成员函数并不在类对象中,所以这里其实没有对指针p解引用,可以理解为是将p指针传递给成员函数,用this接收。
然后我们再来看这个代码的1和2:
函数中,this指针接收到的是空指针,函数是通过this指针访问成员变量的
- 调用Print函数后,我们没有访问类成员对象,因此不会对this指针解引用,所以程序运行正常.
2.调用Year函数后,函数通过this指针访问_year,因此程序会崩。
void Year()
{
cout << "year = " << this->_year;
}