欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++中的this指针原来是这么一回事!

程序员文章站 2024-02-29 17:31:22
...

为什么存在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指针的存在的原因及方式有了一个大概的认识之后,下面再对其特性做一个简单的了解:

  1. this指针的特性:类类型 *const
  2. 只能在普通的成员函数中使用(如静态成员函数中不存在this指针)
  3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象的地址作为实参传递给this形参。所以对象中不存储this指针。
  4. 从上述结论可以得出this指针不影响类的大小,即sizeof()的结果。
  5. 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;
}

结论:

  1. 当以对象.成员函数() 的方式进行调用时,this指针不可以为空。因为对象只要存在就不会为空。
  2. 当以p->成员函数() 的方式进行调用时,this指针可以为空,因为最终是将p作为参数传递给了成员函数,如果p指向的是nullptr,this指针将来就是nullptr。在不通过其访问成员变量或成员函数(成员函数中有成员变量)时并不会报错。