C++中的this指针原来是这么一回事!
为什么存在this指针
对于C++来说,类和对象的相关内容可以说是整个C++语言的灵魂,也是其作为一门面向对象语言的精华所在。那么,一旦谈到类和对象,就少不了对this指针认识及使用了。在探究其特性之前,有必要先对其存在的理由做一个整体的了解,在C++中,this指针到底是什么意思?为什么会存在这样一个this指针呢? 通过下面一段代码来感受一下:
#include <iostream>
using namespace std;
class Student{
public:
void InitStudent(char *n, char *s, int a) {
strcpy(name, n);
strcpy(sex, s);
age = a;
}
void PrintStudent() {
cout << name << "-" << sex << "-" << age << endl;
}
private:
char name[20];
char sex[3];
int age;
};
int main()
{
Student s1, s2;
s1.InitStudent("小王", "男", 19);
s2.InitStudent("小李", "女", 18);
s1.PrintStudent();
s2.PrintStudent();
return 0;
}
上述代码大家都能看懂,就不再做不必要的解释。但现在有这样一个问题,对于代码中的Student类,有InitStudent和PrintStudent俩个成员函数,但是函数体中没有任何关于不同对象的区分,那么当s1调用上述成员函数对自己进行初始化以及打印信息时,该函数又怎么知道是对s1这个对象进行操作而不是s2呢?
带着这个问题,我们先不着急给出答案,由于我们知道,C语言中的结构体和C++中的类非常类似,因此我们不妨先回忆一下对于类似的问题,在C语言中是如何处理的:
#include <stdio.h>
typedef struct Student{
char name[20];
char sex[3];
int age;
}Student;
void InitStudent(Student *ps, char *n, char *s, int a)
{
strcpy(ps->name, n);
strcpy(ps->sex, s);
ps->age = a;
}
void PrintStudent(Student *ps) {
cout << ps->name << "-" << ps->sex << "-" << ps->age << endl;
}
int main()
{
Student s1, s2;
InitStudent(&s1, "小王", "男", 19);
InitStudent(&s2, "小李", "女", 18);
PrintStudent(&s1);
PrintStudent(&s2);
return 0;
}
看到这里,我们不难回忆起,正是通过给相应的函数传递当前结构体变量的地址, 才让对应的函数清楚的知道,当前是在为哪一个变量进行相关操作,从而准确无误的对当前传入的结构体变量进行初始化及信息打印。
可是说到头,这只是C语言中的处理方式,而在C++中,我可没看到哪里给成员函数传递了对象的地址之类的行为啊。但这正是我要说的,其实,对于上述同样的问题,C++的处理方式和C语言比起来,可以说其实是“换汤不换药”,C语言通过传递当前结构体变量的地址来区分对不同变量的操作,而C++中则通过引入this指针来解决该问题。
什么意思呢?C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
说的直白一点,就是说,在实际调用成员函数期间,隐式的传递了当前对象的地址,而成员函数的通过隐藏的this指针来接受这个地址从而能够使成员函数区分不同的对象。
this指针的特性
在对this指针的存在的原因及方式有了一个大概的认识之后,下面再对其特性做一个简单的了解:
- this指针的特性:类类型 *const
- 只能在普通的成员函数中使用(如静态成员函数中不存在this指针)
- this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象的地址作为实参传递给this形参。所以对象中不存储this指针。
- 从上述结论可以得出this指针不影响类的大小,即sizeof()的结果。
- this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
在了解了上述特性之后,我们再对一开始给出的那段代码做一个全新的认识,不难理解,编译器在对上述类进行编译时会对其成员函数做一定的修改,主要就是增加this指针:
class Student{
public:
void InitStudent(char *n, char *s, int a) {
strcpy(name, n);
strcpy(sex, s);
age = a;
}
/*编译器修改后
void InitStudent(Student *const this, char *n, char *s, int a) {
strcpy(this->name, n);
strcpy(this->sex, s);
this->age = a;
}
*/
void PrintStudent() {
cout << name << "-" << sex << "-" << age << endl;
}
/*编译器修改后
void PrintStudent(Student *const this) {
cout << this->name << "-" << this->sex << "-" << this->age << endl;
}
*/
private:
char name[20];
char sex[3];
int age;
};
最后一个问题:this指针可以为空吗? 观察下述代码:
class Student{
public:
void TestFunc()
{
cout << "TestFunc():" << this << endl;//00000000
}
void TestThis() {
cout << "TestThis():" << this << endl;//00000000
this->TestFunc();//正常调用,没有问题
//this->age = 20;//程序会奔溃
//this->PrintStudent();//同样会崩溃
}
void InitStudent(char *n, char *s, int a) {
strcpy(name, n);
strcpy(sex, s);
age = a;
}
void PrintStudent() {
cout << name << "-" << sex << "-" << age << endl;
}
private:
char name[20];
char sex[3];
int age;
};
int main()
{
Student s1, s2;
s1.InitStudent("小王", "男", 19);
s2.InitStudent("小李", "女", 18);
s1.PrintStudent();
s2.PrintStudent();
Student *p = nullptr;
p->TestThis();
return 0;
}
结论:
- 当以对象.成员函数() 的方式进行调用时,this指针不可以为空。因为对象只要存在就不会为空。
- 当以p->成员函数() 的方式进行调用时,this指针可以为空,因为最终是将p作为参数传递给了成员函数,如果p指向的是nullptr,this指针将来就是nullptr。在不通过其访问成员变量或成员函数(成员函数中有成员变量)时并不会报错。
上一篇: MySQL中从库延迟状况排查的一则案例