This 指针是在类对象的成员函数(非静态)中,指向对象自身的指针。这样一个指向类对象的指针,有点像一个指向结构体变量的指针。于是为了更好地理解 this 指针,我们可以先尝试用C语言的语法实现C++的类。
C语言对C++类的实现 - this 指针的原理
让我们先在C++里给出一个简单的类 A:
class A{
public:
void setInfo(int info)
{
this->Info = info;
// Info = info; // 在此处完全等价的写法
}
private:
int Info;
};
在C语言中数据成员可以由结构体实现,但成员函数就只能放在结构体外面了。那普通的函数要怎么样访问结构体的成员呢?答案是把结构体变量的指针传入函数:
struct A{
int Info;
};
void A_setInfo(struct A * A_obj, int info)
{
A_obj->Info = info;
}
实际上这个 A_obj 指针就类似于 this 指针。我们回到 C++ 中的成员函数,可以想到:每个成员函数(非静态)相当于都自带了 this 指针的一个参数,在调用类对象的成员函数时会自动给出。之后会学习到的静态 (static) 成员函数无法直接访问数据成员,相应地它不能使用 this 指针。
在成员函数(非静态)里访问数据成员时,加不加上 this-> 都是等价的。即使不加 this->,编译器也是自动加上的(隐式调用)。为了提高代码的可读性,还是加上 this 更好。
成员函数与对象的独立性
原谅我贫乏的语言能力,只能给这么个奇怪的小标题。为了引出正文,让我们先看一段程序:
class A{
public:
void func()
{
cout<<"TEXT"<<endl;
}
private:
int Info;
};
int main() {
A *p = nullptr;
p->func();
}
上面的程序简单来说就是,创建一个空的 A 类对象指针,再调用它的成员函数。
一个好的编译器自然会给出空指针的 warning。但是实际运行程序会发现并没有报错,而是成功地输出了 TEXT。要理解为什么没有报错,可以再次回到C语言的结构体实现:
struct A{
int Info;
};
void A_func(struct A * this)
{
printf("TEXT\n");
}
int main() {
struct A *p = nullptr;
func(p);
}
虽然指针 p 是空的,但是 A_func 函数中的 this 指针仅仅作为参数,函数的调用本身并不依赖于这个指针。回到C++,func 函数虽然属于类 A,但它不像结构体成员那样在空间上依赖于类对象。我们可以把成员函数想象成在类外面的普通函数,而属于类的这个性质,只是给予了成员函数一些特殊的访问权限。
总结一下,类对象是否为空,与成员函数的存在性,是相互独立的。不过要注意,如果成员函数用到了 this(使用了类的数据成员)还是会报错的:
public:
void func()
{
cout<<this->Info<<endl;
}
this 指针的必要场景
上面所展示的 this 指针用法都是可以省略的。但是有个别情况必须使用 this,即需要明确指代对象自己的时候,比如:
重载 operator = 赋值运算符
重载赋值通常需要把当前构造的新对象作为右值返回给 = 的左值,此时需要用 *this 指代自身:
public:
A & operator = (A & other) // 重载赋值=
{
cout << "Operator = Overloading" << endl;
Info = other.Info;
return *this;
}
自身作为函数实参
比如在成员函数中,利用类外的函数创建当前节点的子节点,其中子节点包含指向父节点的指针,此时需要把 this 传入类外的函数:
public:
void creatChild(Type info_)
{
this->numChild++;
Node(father=this, info=info_);
}