C++是在C语言的基础上发展的,所以C++的大部分特点都可以在C语言里找到对应的,第一个C++编译器就是把C++翻译成C进行编译的。
引入
C语言中没有类的概念,只有结构体,函数都是全局函数,没有成员函数。C++翻译成C时,将 class 翻译成 struct,对象翻译成结构体变量,但是对类的成员函数应该如何翻译?对myCar.Modify();这样通过一个对象调用成员函数的语句,又该如何翻译呢?
在C语言中只有全局函数,因此成员函数只能被翻译成全局函数,myCar.Modify();这样的语句也只能被翻译成普通的调用全局函数的语句。
但是如何让翻译后的 Modify 全局函数还能作用在 myCar 这个结构变量上呢?C++为了解决这个问题引入了“this 指针”。下面来看一段 C++ 程序到C 程序的翻译:
class CCar
{
public:
int price;
void SetPrice(int p);
};
void CCar::SetPrice(int p)
{
price= p;
}
int main()
{
CCar car;
car.SetPrice(20000);
return 0;
}
翻译后的C程序:
struct CCar
{
int price;
};
void SetPrice(struct CCar* this, int p)
{
this->price = p;
}
int main()
{
struct CCar car;
SetPrice(&car, 20000);
return 0;
}
可以看出,类被翻译成结构体,对象被翻译成结构变量,成员函数被翻译成全局函数。
但是C程序的全局函数 SetPrice 比 C++ 的成员函数 SetPrice 多了一个参数,就是struct CCar *this,car.SetPrice(20000);被翻译成SetPrice(&car, 20000);,后者在执行时,this 形参指向的正是 car 这个变量,因而达到了 SetPrice 函数作用在 car 变量上的效果。
作用
实际上,现在的C编译器从本质上来说也是按上面的方法来处理成员函数和对成员函数的调用的,即非静态成员函数实际上的形参个数比程序员写的多一个。多出来的参数就是所谓的“this指针”。这个“this指针”指向了成员函数作用的对象,在成员函数执行的过程中,正是通过“Ihis指针”才能找到对象所在的地址,因而也就能找到对象的所有非静态成员变量的地址。
如下:
class A
{
int i;
public:
void Hello()
{
cout << "hello" << endl;
}
};
int main()
{
A* p = NULL;
p -> Hello();
}
上面的程序输出结果是:hello
显然 p 是一个空指针,为何通过它还能正确调用 A 的成员函数 Hello 呢?
参考上面 C++ 到C程序的翻译,p->Hello()实质上应该是Hello§,在翻译后的 Hello 函数中,cout 语句没有用到 this 指针,因此依然可以输出结果。如果 Hello 函数中对成员变量有访问,那么程序就会出错。
总结
- this 指针是一个隐含于每一个非静态成员函数中的特殊指针。它指向调用该成员函数的那个对象。
- 当对一个对象调用成员函数时,编译程序先将对象的地址赋给 this 指针,然后调用成员函数,每次成员函数存取数据成员时,都隐式使用 this 指针。
- 当一个成员函数被调用时,自动向它传递一个隐含的参数,该参数是一个指向这个成员函数所在的对象的指针。
- this 指针被隐含地声明为:
ClassName *const this
,这意味着不能给 this 指针赋值;在 ClassName 类的 const 成员函数中,this 指针的类型为:const ClassName* const
,这说明不能对 this 指针所指向的这种对象是不可修改的(即不能对这种对象的数据成员进行赋值操作); - this 并不是一个常规变量,而是个右值,所以不能取得 this 的地址(不能 &this)。
- 在以下场景中,经常需要显式引用 this 指针:
- 为实现对象的链式引用;
- 为避免对同一对象进行赋值操作;
- 在实现一些数据结构时,如 list。